In one of the previous articles I discussed the basics of HTTP modules. As the power of Nginx comes from configuration files, you definitely need to know how to configure your module and make it ready for variety of environments that users of your module can have. Here is how you can define configuration directives. A configuration directive is described by the structure ngx_command_t:
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; }; #define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } typedef struct ngx_command_s ngx_command_t;
Here name is name of the directive, type is type of the directive, number of arguments and blocks where it might appear. The value of this field is a combination of the following flags:
Flag | Meaning |
NGX_CONF_NOARGS | The directive does not take any arguments |
NGX_CONF_TAKE1 … NGX_CONF_TAKE7 | The directive takes specified number of arguments |
NGX_CONF_TAKE12 | The directive takes 1 or 2 arguments |
NGX_CONF_TAKE13 | The directive takes 1 or 3 arguments |
NGX_CONF_TAKE123 | The directive takes 1, 2 or 3 arguments |
NGX_CONF_TAKE1234 | The directive takes 1, 2, 3 or 4 arguments |
NGX_CONF_BLOCK | An additional argument is a block |
NGX_CONF_FLAG | The directive is a flag (has only values ‘on’ and ‘off’) |
NGX_CONF_ANY | The directive takes 0 or more arguments |
NGX_CONF_1MORE | The directive takes 1 or more arguments |
NGX_CONF_2MORE | The directive takes 2 or more arguments |
NGX_DIRECT_CONF | The directive might be specified only in the main configuration file |
NGX_MAIN_CONF | The directive might be specified only on the main configuration level |
NGX_ANY_CONF | The directive might be specified on any configuration level |
NGX_HTTP_MAIN_CONF | The directive might be specified on main level of HTTP-server configuration |
NGX_HTTP_SRV_CONF | The directive might be specified on virtual server level of HTTP-server configuration |
NGX_HTTP_LOC_CONF | The directive might be specified on location level of HTTP-server configuration |
NGX_HTTP_LMT_CONF | The directive might be specified in limit_except block |
NGX_HTTP_LIF_CONF | The directive might be specified in if() block |
set is a handler that Nginx calls whenever it discovers this directive. This handler must return NGX_CONF_OK in case of success (all arguments are correct, no error occured), NGX_CONF_ERROR if there is any error or arbitrary constant string that will be also considered as an error. The string then will be printed in the error message. Additionally you can use a function ngx_conf_log_error to print an error message.
For convenience there is a number of standard handlers implemented that you can use:
Name of the handler |
Data type |
Type of the field in the configuration that offset points to |
ngx_conf_set_flag_slot | Flag | ngx_flag_t |
ngx_conf_set_str_slot | String | ngx_str_t |
ngx_conf_set_str_array_slot | Array of srings | Pointer to ngx_array_t -> ngx_str_t |
ngx_conf_set_keyval_slot | Key-value pair vector | Pointer to ngx_array_t -> ngx_keyval_t |
ngx_conf_set_num_slot | Signed integer | ngx_int_t |
ngx_conf_set_size_slot | Length | size_t |
ngx_conf_set_off_slot | Offset | off_t |
ngx_conf_set_msec_slot | Milliseconds | ngx_msec_t |
ngx_conf_set_sec_slot | Seconds | time_t |
ngx_conf_set_bufs_slot | Number and size of buffers | ngx_bufs_t |
ngx_conf_set_enum_slot | Enumeration | ngx_uint_t |
ngx_conf_set_bitmask_slot | Bitmap | ngx_uint_t |
ngx_conf_set_path_slot | Path in the filesystem and number or characters in hashed directory name | ngx_path_t |
ngx_conf_set_access_slot | Access right | ngx_uint_t |
conf — is the level of configuration that the directive refers to or 0 if it doesn’t refer to any configuration level;
offset — the offset of the field in the configuration data structure of the module that this directive corresponds to. To calculate the offset you can use the standard offsetof macro;
post — a pointer to a post-processing handler.
A list of directives of a module is described by a vector of structures ngx_command_t that is terminated with a value ngx_null_command. Example:
static ngx_command_t ngx_http_sample_module_commands[] = { { ngx_string("foo"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_sample_module_loc_conf_t, foo), NULL }, ngx_null_command };
Here we described a directive foo, that takes 1 argument. The directive will be allowed to appear on HTTP configuration level, virtual server configuration and location configuration. The argument of the directive will be converted into a string and stored into the field foo of module’s location configuration structure.
Once the field commands of ngx_module_t structure is initialized with a pointer to a list of module’s directives (see this article), Nginx will look for these directives while parsing the configuration file.
Nginx processes configuration file from top to bottom and calls handlers of corresponding configuration directives. A handler is allowed to do anything it wants with the configuration, as long as it knows where the data to be manipulated are located. A handler is also allowed to create a new configuration level and process a configuration block that corresponds to that level.
As you can see the behavior of the directive is totally determined by it’s handler, the conf and offset fields are here only for convenience. This makes complex things possible, while making sure that simple configuration parameters are implemented simply with the help of the standard handlers.
When you want to customise a simple parameter of a module, it is sufficient to describe a configuration directive and use one of the standard configuration handlers. Sometimes you might want to implement a handler that activates your module in certain location, e.g.:
static char * ngx_http_sample_module_command(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_sample_handler; return NGX_CONF_OK; }
Configuration handlers also can process blocks of a configuration file (block is delimited by curly brackets: { } ). Blocks can be handful when the configuration of your module is very complex, such as the configuration of the HTTP module, at which we will take a look later.