nginx phase handler processing and log phase processing

Posted by Coldman on Thu, 06 Jan 2022 03:49:43 +0100

The phase of nginx has eleven processing stages, using NGX_ http_ phase_ The T structure stores the handler available for each stage, which is actually a dynamic array ngx_array_t. The element type is ngx_http_handler_pt, stored in NGX_ http_ core_ main_ conf_ In t:

typedef struct {

    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */

    ngx_http_phase_engine_t    phase_engine;

    ngx_hash_t                 headers_in_hash;

    ngx_hash_t                 variables_hash;


    ngx_array_t                variables;         /* ngx_http_variable_t */

    ngx_array_t                prefix_variables;  /* ngx_http_variable_t */

    ngx_uint_t                 ncaptures;


    ngx_uint_t                 server_names_hash_max_size;

    ngx_uint_t                 server_names_hash_bucket_size;

    ngx_uint_t                 variables_hash_max_size;

    ngx_uint_t                 variables_hash_bucket_size;


    ngx_hash_keys_arrays_t    *variables_keys;

    ngx_array_t               *ports;

    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

} ngx_http_core_main_conf_t;

typedef struct {

    ngx_array_t                handlers;

} ngx_http_phase_t;

This is actually a two-dimensional linked list:

Engine processing

Using two-dimensional array phases can call all handler modules, but its organization is not flexible and efficient. In fact, nginx does not directly use handler, but implements a specific checker function for each stage, calls handler in the checker, and realizes the flexible jump of the stage according to the return value.

//Store the handler/checker, which uses next to realize the fast jump of the stage

struct ngx_http_phase_handler_s {

    // checker function of stage

    ngx_http_phase_handler_pt  checker;

    // Each module has its own processing function

    ngx_http_handler_pt        handler;

    // Point to the position of the first module in the array in the next stage

    ngx_uint_t                 next;

};

//All http requests are processed using this engine

typedef struct {

    // Store the array of all handlers / checkers, and use next to realize the fast jump of the stage

    ngx_http_phase_handler_t  *handlers;

    // Jump location of server override

    ngx_uint_t                 server_rewrite_index;

    // location override jump position

    ngx_uint_t                 location_rewrite_index;

} ngx_http_phase_engine_t;

Engine initialization:

When parsing to http {} block, NGX_ http_ The block () function will call the postconfiguration function of all modules to add its handler processing function to the phases array.

Then nginx executes the function ngx_http_init_phase_handlers(), this function will traverse the phases array, calculate the number of handler modules, count the number of registered handlers, allocate memory, install stage classification, combine each handler with the checker of the corresponding stage, and fill in the engine array.

For example, suppose post_ In the read} phase, three modules have registered callback functions and server respectively_ If two modules of rewrite have registered callback functions, then ph[0-2] is in post_ In the read phase, ph[3-4] is in the SERVER_REWRITE phase.

All NGX of the previous stage_ http_ phase_ handler_ The next in s points to the beginning of the next stage. For example, the value of the following three is 3, and sequence number 3 is just the server of the next stage_ Rewrite starts with the serial number.

ph[0]->next = 3

ph[1]->next = 3

ph[2]->next = 3

Take a runtime example.

Contents of the runtime ph array
Array subscript , checker , handler , next

Engine running mechanism

ngx_http_core_run_phases:

ngx_ http_ request_ Member phase in t_ Handler marks the handler sequence number of the stage in the current processing process, starting from 0, usually NGX_ HTTP_ SERVER_ REWRITE_ The first module of phase ngx_http_rewrite_module.

After receiving the request header, nginx starts calling the function ngx_http_core_run_phases execution engine.

//Start engine array processing request

//From phase_ The location of the handler starts calling the module processing

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;



    // Get the core main configuration

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);



    // Get the handler array in the engine

    ph = cmcf->phase_engine.handlers;



    // From phase_ The location of the handler starts calling the module processing

    // The starting sequence number of the engine array of the external request is 0. Execute the engine array from scratch, that is, start with Post read

    // Internal request, i.e. sub request Skip post read and directly start from server rewrite, that is, find the server

    while (ph[r->phase_handler].checker) {

        // Call the checker in the engine array

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        // The checker checks the return value of the handler

        // If the handler returns again/done, it returns ok

        // Exit processing of engine array

        // Because R - > write_ event_ handler = ngx_ http_ core_ run_ phases

        // When there is another write event, it will continue to execute from the previous module

        // If the checker returns again, continue to execute in the engine array

        // The module consists of R - > phase_ The handler sequence number specifies that there may be stage jumps

        if (rc == NGX_OK) {

            return;

        }

    }

}

The checker processes at different stages are similar. Let's take a look at NGX_ HTTP_ CONTENT_ NGX of phase_ http_ core_ content_ phase().

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;



    // Check whether the request has a handler, that is, the handler is defined in the location

    if (r->content_handler) {

        // Set write event to ngx_http_request_empty_handler

        // That is, it will not enter NGX for the time being_ http_ core_ run_ phases

        // This is because the content generation stage is already the "last" stage, and there is no need to go through other stages

        // After that, it will be changed to NGX when sending data_ http_ set_ write_ handler

        // But we can also modify it so that the write event triggers our own callback

        r->write_event_handler = ngx_http_request_empty_handler;



        // Call the content processing handler dedicated to location

        // The return value is passed to ngx_http_finalize_request

        // It is equivalent to ending the request after processing

        // This usage simplifies the client code and is equivalent to the template method pattern

        // rc = handler(r); ngx_http_finalize_request(rc);

        // End request

        // But if count > 1, it will not really end

        // handler may return done and again, for example, call read body

        ngx_http_finalize_request(r, r->content_handler(r));



        // You need to continue processing in the subsequent processing function. You cannot call write_event_handler

        return NGX_OK;

    }



    // There is no special handler

    // Call each module's own processing function

    rc = ph->handler(r);

    if (rc != NGX_DECLINED) {

        ngx_http_finalize_request(r, rc);

        return NGX_OK;

    }

    // Module handler returns decline. You need to continue to execute the next handler in this stage.

    ph++;



    // The next module has a checker

    if (ph->checker) {

        // Index plus 1

        r->phase_handler++;

        // again continues the cycle of the engine array

        return NGX_AGAIN;

    }

    // You have reached the end of the engine array

    // No content module can handle it

    // End cycle of engine array

    return NGX_OK;

}

Log processing phase

Log processing is not in NGX_ http_ core_ run_ Called in phases, but when the request is completed.

//The request has ended. Call the log module to record the log

//In ngx_http_finalize_request and NGX_ http_ free_ Call in request

static void

ngx_http_log_request(ngx_http_request_t *r)

{

    ngx_uint_t                  i, n;

    ngx_http_handler_pt        *log_handler;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    // The log handler is not in the engine array

    log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;

    n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;



    // Instead of checking the return value of the handler, call directly without using the checker

    for (i = 0; i < n; i++) {

        log_handler[i](r);

    }

}

The log phase is called after a response is given

Configuration resolution phase:

static ngx_int_t

ngx_http_log_init(ngx_conf_t *cf)

{

ngx_str_t   *value;

ngx_array_t a;

ngx_http_handler_pt   *h;

ngx_http_log_fmt_t   *fmt;

ngx_http_log_main_conf_t   *lmcf;

ngx_http_core_main_conf_t  *cmcf;



lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);



if (lmcf->combined_used) {

if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {

return NGX_ERROR;

}



value = ngx_array_push(&a);

if (value == NULL) {

return NGX_ERROR;

}



*value = ngx_http_combined_fmt;

fmt = lmcf->formats.elts;



if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)

!= NGX_CONF_OK)

{

return NGX_ERROR;

}

}



cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);



h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);

if (h == NULL) {

return NGX_ERROR;

}



*h = ngx_http_log_handler;



return NGX_OK;

}

Topics: Nginx