This is the 24th day of my participation in Gwen Challenge

background

This is the sixth Nginx learning notes collection, continuing the previous article with notes related to custom development of Nginx plug-ins

Custom development of Nginx plug-ins

Module context structure

The context structure of a module is stored in a static variable of type ngX_HTTP_module_T

typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t; 
Copy the code

It is a pointer to a set of callback functions, including those that create objects that store configuration information, and those that are called before and after creation, all of which will be called by Nginx at the appropriate time

Postconfiguration: create_main_conf: create_main_conf: postconfiguration: create_main_conf: postconfiguration: create_main_conf: postconfiguration: create_main_conf Call this function to create the configuration information storage structure of the module in the HTTP block. On success, this function returns the created configuration object, on failure, NULL init_main_conf: Call this function to initialize the configuration information storage structure of the module in the HTTP block. On success, NGX_CONF_OK is returned; on failure, NGX_CONF_ERROR or create_srv_conf is returned: Call this function to create the configuration information store structure of the module in the HTTP server block. Each server block creates one. This function returns the created configuration object on success, and NULL merge_srv_conf on failure: Since some configuration instructions can appear in either HTTP Block or HTTP Server block, each server will have its own storage structure to store the configuration of that server, but in this case the configuration of the HTTP Block is the same as that of the server When configuration information in a block conflicts, this function needs to be called to merge. This function does not have to be provided, but it is not required to be provided when it is expected that the need to merge will never occur. For security purposes, this function returns NGX_CONF_OK on success and NGX_CONF_ERROR or create_loc_conf on failure: Call merge_loc_conf to create a configuration structure for each location specified in the configuration block. This function returns the created configuration object on success, or NULL on failure: Similar to merge_srv_conf, this is where configuration value merging takes place, returning NGX_CONF_OK on success and NGX_CONF_ERROR or an error string on failureCopy the code

Nginx configuration information is nested layer by layer, for a specific location, for the same configuration, if the current level is not defined, then use the upper level configuration, otherwise use the current level configuration This configuration information should be set to an uninitialized value by default. For this purpose,Nginx defines a series of macro definitions to represent the uninitialized value of the data type for each configuration:

#define NGX_CONF_UNSET       -1
#define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1
#define NGX_CONF_UNSET_PTR   (void *) -1
#define NGX_CONF_UNSET_SIZE  (size_t) -1
#define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1
Copy the code

If have configured in this level, that is, the value of the configuration items have been read came in, just use the level of value as a result of incorporating definitions, otherwise, use the upper value, if the value of the upper and the UNSET the value of the class that is assigned as the default value, otherwise, use the upper value as a result of the merger, for such similar operations, Nginx defines a Some macro operations to do this, for example:

#define ngx_conf_merge_uint_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET_UINT) { \ conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; The \}Copy the code

Definition of a context named Hello module:

static ngx_http_module_t ngx_http_hello_module_ctx = {
    NULL,                          /* preconfiguration */
    ngx_http_hello_init,           /* postconfiguration */

    NULL,                          /* create main configuration */
    NULL,                          /* init main configuration */

    NULL,                          /* create server configuration */
    NULL,                          /* merge server configuration */

    ngx_http_hello_create_loc_conf, /* create location configuration */
    NULL                        /* merge location configuration */
};
Copy the code

Note: the merge_loc_conf function is not provided here, because the configuration instructions for this module are determined to occur only at the NGX_HTTP_LOC_CONF level, and no need to merge occurs

Module definition

Development module need to define a ngx_module_t variables of type information to illustrate the module itself, this is the module is one of the most important information, which tells the Nginx the module is some information, the above definition of configuration information, and module context information is through this structure to tell Nginx system, namely load module The upper layer code needs to get this information through the structure defined

Ngx_module_t structure definition:

typedef struct ngx_module_s      ngx_module_t;
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            abi_compatibility;
    ngx_uint_t            major_version;
    ngx_uint_t            minor_version;
    void                 *ctx;
    ngx_command_t        *commands;
    ngx_uint_t            type;
    ngx_int_t           (*init_master)(ngx_log_t *log);
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    void                (*exit_thread)(ngx_cycle_t *cycle);
    void                (*exit_process)(ngx_cycle_t *cycle);
    void                (*exit_master)(ngx_cycle_t *cycle);
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

#define NGX_NUMBER_MAJOR  3
#define NGX_NUMBER_MINOR  1
#define NGX_MODULE_V1          0, 0, 0, 0,                              \
    NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0
Copy the code

Example: The module definition of the Hello module

ngx_module_t ngx_http_hello_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_module_ctx,    /* module context */
    ngx_http_hello_commands,       /* module directives */
    NGX_HTTP_MODULE,               /* module type */
    NULL,                          /* init master */
    NULL,                          /* init module */
    NULL,                          /* init process */
    NULL,                          /* init thread */
    NULL,                          /* exit thread */
    NULL,                          /* exit process */
    NULL,                          /* exit master */
    NGX_MODULE_V1_PADDING
};
Copy the code

Modules can provide callback functions to Nginx that are called when Nginx creates or terminates a process thread, but most modules don’t need to do anything at these times, so they simply assign the value NULL

Handler handler function

The handler module must provide a handler function for handling requests from clients. This function can either generate content directly, reject it and let subsequent handlers handle it, or dump it to a subsequent filter for processing

Function prototype declaration:

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
Copy the code

R is the HTTP request, which contains all the information requested The function returns NGX_OK on success, NGX_ERROR on error, and NGX_DECLINE on rejection. Otherwise, NGX_ERROR indicates that the response to the client has been generated

Mount the Handler module

The handler module’s handler functions are mounted to the process in two ways:

  • Mount by processing stage
  • According to the need to mount

Mount the Handler module by processing stage

In order to fine-control the processing of client requests,Nginx divides the processing into 11 stages, from front to back:

NGX_HTTP_POST_READ_PHASE: read request content phase NGX_HTTP_SERVER_REWRITE_PHASE: Server request address rewrite phase NGX_HTTP_FIND_CONFIG_PHASE: NGX_HTTP_REWRITE_PHASE: Location Request address rewrite phase NGX_HTTP_POST_REWRITE_PHASE: Request address rewrite submission phase NGX_HTTP_PREACCESS_PHASE: Access check Preparation phase NGX_HTTP_ACCESS_PHASE: Access check phase NGX_HTTP_POST_ACCESS_PHASE: Access check submission phase NGX_HTTP_TRY_FILES_PHASE: Try_files Processing phase NGX_HTTP_CONTENT_PHASE: content generation phase NGX_HTTP_LOG_PHASE: log module processing phaseCopy the code

Custom modules are mostly mounted in the NGX_HTTP_CONTENT_PHASE, and the mounting action is usually in the postConfiguration function called in the module context

Note: There are a few phases that are special in that they don’t call any of the handler’s at the mount point, so you don’t need to mount them:

NGX_HTTP_FIND_CONFIG_PHASE
NGX_HTTP_POST_ACCESS_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_TRY_FILES_PHASE
Copy the code

So there are really seven phases to mount the handler

The mounted code is as follows:

static ngx_int_t
ngx_http_hello_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    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_hello_handler;

    return NGX_OK;
}
Copy the code

Handlers mounted in this way are also called Content Phase Handlers

Mount handler modules as required

Handlers mounted in this way are also called content Handler, when a request comes in,Nginx executes all of the handlers in each phase, starting with the NGX_HTTP_POST_READ_PHASE phase, to the NGX_HTTP_CONTENT_PHASE phase if the location has a co Ntent Handler module, then execute the content Handler module’s actual handler function, otherwise continue to execute all content Phases in the NGX_HTTP_CONTENT_PHASE phase in sequence Handlers, until a function processing returns either NGX_OK or NGX_ERROR, i.e., when a location is processed to the NGX_HTTP_CONTENT_PHASE, if there is content Handler module, then all Content Phase Handlers mounted by NGX_HTTP_CONTENT_PHASE are not executed

The handler mounted using this method has a feature that it must be executed in the NGX_HTTP_CONTENT_PHASE phase. If you want your handler to be executed in an earlier phase, you should not mount this method

Use scenario: for example, when a module handles a location and finds that it is logical to handle it and there is no need to call other handlers in the NGX_HTTP_CONTENT_PHASE phase to handle it, it dynamically mounts this handler

Mount the sample

static char *
ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_circle_gif_handler;

    return NGX_CONF_OK;
}
Copy the code

This section describes how to write handler modules

The steps to implement a Handler module;

  1. Write basic module structure: including module definition, module context structure, module configuration structure, etc
  2. Implement the mount function of handler: Select the correct mount party according to the requirements of the module
  3. Write the handler function: this function is used to complete the function of the module