Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

The basic structure of the Nginx module


May 23, 2021 Nginx Getting started


Table of contents


The basic structure of the module

In this section we will explain some of the commonly used parts of each module during the usual module development process. S ome of these parts are necessary and some are not. These things are also listed here for other types of modules, such as filter modules.

The module configuration structure

Basically, each module provides configuration instructions so that the user can control the behavior of the module through configuration. S o how is this configuration information stored? Then you need to define the configuration structure of the module for storage.

Everyone knows that Nginx's configuration information is divided into several scopes (scope, sometimes called context), which are main, server, and location. T he same configuration instructions provided by each module can also appear in these scopes. F or the configuration information for these three scopes, each module needs to define three different data structures to store. O f course, not every module provides configuration instructions in all three scopes. T hen it is not necessarily enough that each module needs to define three data structures to store this configuration information. Depending on the implementation of the module, a few need to be defined.

One thing to note is that during module development, we'd better use Nginx's original naming habits. This fits the original code better and looks more comfortable.

For the definition of module configuration information, the naming habit is ngx_http_<module name>_(main|srv|loc)_conf_t Here's an example from the hello module that we're going to show you later.

    typedef struct
    {
        ngx_str_t hello_string;
        ngx_int_t hello_counter;
    }ngx_http_hello_loc_conf_t;

Module configuration instructions

The configuration instructions for a module are defined in a static array. S imilarly, let's look at the definition of module configuration instructions intercepted from hello module.

    static ngx_command_t ngx_http_hello_commands[] = {
       { 
            ngx_string("hello_string"),
            NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
            ngx_http_hello_string,
            NGX_HTTP_LOC_CONF_OFFSET,
            offsetof(ngx_http_hello_loc_conf_t, hello_string),
            NULL },

        { 
            ngx_string("hello_counter"),
            NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
            ngx_http_hello_counter,
            NGX_HTTP_LOC_CONF_OFFSET,
            offsetof(ngx_http_hello_loc_conf_t, hello_counter),
            NULL },               

        ngx_null_command
    };

In fact, looking at this definition, you can basically see some information. F or example, we have defined two configuration instructions, one called hello_string, which can accept one argument, or no argument. A nother command is the hello_counter, which accepts a NGX_CONF_FLAG type of argument. O ther than that, it seems a little confusing. It doesn't matter, let's take a look at ngx_command_t in detail, and once we understand the details of this structure, I believe that all the information expressed in this definition speaks for itself.

ngx_command_t definition of the src/core/ngx_conf_file.h

    struct ngx_command_s {
        ngx_str_t             name;
        ngx_uint_t            type;
        char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
        ngx_uint_t            conf;
        ngx_uint_t            offset;
        void                 *post;
    };

name: The name of the configuration instruction.

Type : The type of configuration, more accurately, is a collection of the configuration instruction properties. N ginx provides a number of predefined property values (some macro definitions) that can be combined by logic or operators to form a detailed description of this configuration instruction. The predefined property values and descriptions that can be used here are listed below.

  • NGX_CONF_NOARGS: The configuration instruction does not accept any parameters.
  • NGX_CONF_TAKE1: The configuration instruction accepts 1 parameter.
  • NGX_CONF_TAKE2: The configuration instruction accepts 2 parameters.
  • NGX_CONF_TAKE3: The configuration instruction accepts 3 parameters.
  • NGX_CONF_TAKE4: The configuration instruction accepts 4 parameters.
  • NGX_CONF_TAKE5: The configuration instruction accepts 5 parameters.
  • NGX_CONF_TAKE6: The configuration instruction accepts 6 parameters.
  • NGX_CONF_TAKE7: The configuration instruction accepts 7 parameters.

You can combine multiple properties, such as an instruction that can accept one or two parameters without filling in parameters. S o NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2 If writing the above three properties together and you find it cumbersome, then it doesn't matter, Nginx provides some definitions that are more concise to use.

  • NGX_CONF_TAKE12: The configuration instruction accepts 1 or 2 parameters.
  • NGX_CONF_TAKE13: The configuration instruction accepts 1 or 3 parameters.
  • NGX_CONF_TAKE23: The configuration instruction accepts 2 or 3 parameters.
  • NGX_CONF_TAKE123: The configuration instruction accepts 1 or 2 or 3 parameters.
  • NGX_CONF_TAKE1234: The configuration instruction accepts 1 or 2 or 3 or 4 parameters.
  • NGX_CONF_1MORE: The configuration instruction accepts at least one parameter.
  • NGX_CONF_2MORE: The configuration instruction accepts at least two parameters.
  • NGX_CONF_MULTI: Configuration instructions can accept multiple parameters, i.e. indeterms.
  • NGX_CONF_BLOCK: The value acceptable to the configuration instruction is a configuration information block. T hat's what a pair of braces are enclosed. T here can be a lot of configuration instructions. This is the case, for example, with common server instructions.
  • NGX_CONF_FLAG: The value acceptable to the configuration instruction is "on" or "off" and is eventually converted to a bool value.
  • NGX_CONF_ANY: Configure any parameter values that the instruction can accept. One or more, or "on" or "off," or configuration blocks.

Finally, in any case, Nginx's configuration instructions must not have more than NGX_CONF_MAX_ARGS parameters. This value is currently defined as 8, i.e. no more than 8 parameter values.

The following describes a set of properties that describe where configuration instructions can appear.

  • NGX_DIRECT_CONF: can appear in the outerst layer of the profile. For example, the configuration instructions daemon that have been provided, master_process, and so on.
  • NGX_MAIN_CONF: http, mail, events, error_log, etc.
  • NGX_ANY_CONF: This configuration instruction can appear at any configuration level.

For most of the modules we write, we're dealing with http-related things, called NGX_HTTP_MODULE, and for modules of this type, the configuration can be configured in places that appear directly inside http, as well as in other locations.

  • NGX_HTTP_MAIN_CONF: can appear directly in the http configuration instruction.
  • NGX_HTTP_SRV_CONF: can appear in the server configuration instructions inside http.
  • NGX_HTTP_LOC_CONF: Can appear in the location configuration instructions inside the http server block.
  • NGX_HTTP_UPS_CONF: can appear in the upstream configuration instructions inside http.
  • NGX_HTTP_SIF_CONF: The if statement that can appear in the server configuration instruction inside http is in the block.
  • NGX_HTTP_LMT_CONF: can appear in the block of limit_except instructions inside http.
  • NGX_HTTP_LIF_CONF: The if statement that can appear in the location configuration instruction inside the http server block is in the block.

set : This is a function pointer, and when Nginx parses the configuration, if this configuration instruction is encountered, the read value is passed to the function for decomposition. B ecause the value of each configuration instruction is handled, only the person who defines the configuration instruction is the clearest. Take a look at the function prototype required by this function pointer.

char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

Look at the return value of the function first, and when processing is successful, NGX_OK is returned, otherwise NGX_CONF_ERROR or a string of custom error messages is returned.

Take a look at the three incoming arguments when this function is called.

  • cf: The parameter holds the original string read from the configuration file and some related information. Of particular note is that the args field of this parameter is an array of ngx_str_t types, the first element of which is the configuration instruction itself, the second element is the first argument of the instruction, and the third element is the second argument, and so on.

  • cmd: This configuration instruction corresponds to ngx_command_t structure.

  • conf: is the structure that is defined to store this configuration value, such as the one shown ngx_http_hello_loc_conf_t. W hen this hello_string is resolved, the incoming conf points to a variable ngx_http_hello_loc_conf_t type of file. Users can use type conversion to convert to a type they know, and then assign fields.

To make it easier to read configuration instruction parameters, Nginx already provides by default functions that read parameters of some standard types and can be assigned directly to the set field. Here's a look at these implemented set-type functions.

  • ngx_conf_set_flag_slot: Read the NGX_CONF_FLAG of the type.
  • ngx_conf_set_str_slot: Read the parameters of the string type.
  • ngx_conf_set_str_array_slot: Read the parameters of the string array type.
  • ngx_conf_set_keyval_slot: Read key value pair parameters for type.
  • ngx_conf_set_num_slot: Read the parameters of the integer type (ngx_int_t integer).
  • ngx_conf_set_size_slot: Read the size_t of the type, which is the number of symbols.
  • ngx_conf_set_off_slot: Read the parameters off_t type of computer.
  • ngx_conf_set_msec_slot: Read the parameters of the millisecond value type.
  • ngx_conf_set_sec_slot: Read the parameters of the second value type.
  • ngx_conf_set_bufs_slot: The number of parameter values read is 2, one is the number of bufs, and one is the size of the buf. For example: output_buffers 1 128k;
  • ngx_conf_set_enum_slot: Read the parameters of the enumeraled type and convert them to ngx_uint_t type.
  • ngx_conf_set_bitmask_slot: Read the values of parameters and store the values of those parameters as bit bit bits. For example, the http DavModule module's dav_methods instructions.

conf : This field is used by the NGX_HTTP_MODULE type module (we write basically NGX_HTTP_MOUDLE, and only some Nginx core modules are non-NGX_HTTP_MODULE), which specifies the memory location stored by the current configuration item. I t is actually a question of which memory pool to use. B ecause the http module stores the configuration information to be stored for all http modules, it is stored in three places: main, server, and location, each with a memory pool to allocate the memory to store that information. T he possible values here NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET, or NGX_HTTP_LOC_CONF_OFFSET. Of course, you can also set it directly to 0, which is NGX_HTTP_MAIN_CONF_OFFSET.

offset : Specifies the exact location of the configuration item value, typically specified as a field offset for a structure variable. B ecause for the storage of configuration information, we generally define a structure to store. S o for example, we define a structure A, and the value of that configuration needs to be stored in the structure's b field. T hen you can fill in offsetof (A, b) here. For some configuration items, its value can be set to 0 when it does not need to be saved or needs to be saved to a more complex structure.

post: The field stores a pointer. Y ou can point to any data that is required during the read configuration process to facilitate the processing of the configuration read. Most of the time, you don't need it, so simply set it to 0.

See here, it should be clearer. n gx_http_hello_commands this array is a group of every 5 elements used to describe all the conditions of a configuration item. If you have more than one configuration item, just add 5 more corresponding elements to describe the new configuration item as needed.

It is important to note that at ngx_http_hello_commands end of the array definition, an ngx_null_command is added.

The module context structure

This is a static ngx_http_module_t of the type. T his variable actually provides a set of callback function pointers, from functions that create objects that store configuration information to functions that are called before and after creation. These functions will be called by Nginx at the appropriate time.

    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; 
  • Preconfiguration: Called before the module's configuration information is created and read.

  • Postconfiguration: Called after creating and reading configuration information for the module.

  • create_main_conf: Call the function to create a configuration information store structure for this module at http block. W hen the function succeeds, the created configuration object is returned. If it fails, return NULL.

  • init_main_conf: Call the function to initialize the configuration information storage structure of this module located at http block. W hen the function succeeds, the NGX_CONF_OK. If it fails, return NGX_CONF_ERROR or error string.

  • create_srv_conf: Call this function to create a configuration information store structure for this module located at http server block, and one for each server block. W hen the function succeeds, the created configuration object is returned. If it fails, return NULL.

  • merge_srv_conf: Because some configuration instructions can appear neither in the http block nor in the http server block. I n this case, each server will have its own storage structure to store the configuration of the server, but in this case the configuration in the http block conflicts with the configuration information in the server block, this function needs to be called for consolidation, which is not required, and is not required when it is not expected that there will be any need to merge. O f course, for security reasons or advice. W hen the function executes successfully, the NGX_CONF_OK. If it fails, return NGX_CONF_ERROR or error string.

  • create_loc_conf: Call the function to create a configuration information store structure for this module at location block. E ach location specified in the configuration creates one. T he function executes successfully and returns the configured object that was created. If it fails, return NULL.

  • merge_loc_conf: Similar to merge_srv_conf, this is where configuration values are merged. W hen the function succeeds, the NGX_CONF_OK. If it fails, return NGX_CONF_ERROR or error string.

The configuration information in Nginx is nested on the next layer, and for a specific location, if the current level is not defined, then the upper layer configuration is used, otherwise the configuration of the current level is used.

For this requirement, Nginx defines a series of macro definitions to represent uninitialized values for the data types for each configuration, which should generally default to an uninitialized value:

    #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

And because the logic is similar for the merge of configuration items, that is, as already said earlier, if the values of the configuration items have been configured at this level, that is, the values of the configuration items have been read in (then the values of these configuration items will not be equal to those unSET values defined above), the values of this level will be used as the result of defining the merge, otherwise, the upper values will be used, if the upper values are also the values of these UNSET classes, then the values of the upper layers will be the default values, otherwise the values of the upper layers will be used as the result of the merge. For something like this, Nginx defines some macro operations to do these things, and let's look at one of the definitions.

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

Obviously, the logic is simpler, so other macro definitions are similar, so let's list some of them.

    ngx_conf_merge_value
    ngx_conf_merge_ptr_value
    ngx_conf_merge_uint_value
    ngx_conf_merge_msec_value
    ngx_conf_merge_sec_value

Wait a minute.

Let's take a look at the definition of the module context of the hello module to get a better impression.

    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 */
    };

Note: The merge_loc_conf function is not available here, because the configuration instructions for this module have determined that only appear at this level in NGX_HTTP_LOC_CONF and that there will be no cases that require consolidation.

The definition of the module

For the development of a module, we all need to define a ngx_module_t-type variable to describe the information of the module itself, in a sense, this is the most important information of this module, it tells Nginx this module some information, the configuration information defined above, as well as the module context information, are through this structure to tell the Nginx system, that is, the upper layer of the loading module code, all need to be defined by this structure, to obtain this information.

Let's look at the definition ngx_module_t the next step

    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

Take a look at 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
    };

The module can provide some callback functions to Nginx, which is called when it creates a process thread or ends a process thread. But most modules don't need to do anything at these times, so they're simply assigned NULL.