Nginx Module Extension Sample Chapter
Nginx Module Extension Sample Chapter
Usama Dar
At the end of this chapter, the advanced users will have an idea about internal Nginx architecture, and what is the basis of creating your own third-party module. Readers should be able to know how to use NDK; the source code will help them see a very simple self-written module in action.
Module delegation can also be called module chaining. The core pretty much does the basic stuff related to setting up the connection and taking care of things related to the protocol. It then sets up a chain of modules to execute, each taking care of a certain phase or stage of request processing. The module-based noncentralized architecture makes it possible for advanced users to develop a module that does something they want. The following are the different types of Nginx modules.
Handlers
There is a handler for each dened location in the conguration le. When the server starts up, handlers are attached or bound to a location. Ideally there should only be one handler to a location; if there are more than one dened in the conguration le, only one of them will be valid (typically the last one). Handlers end in the following three ways: successfully when all is good, fail when there is an error, or they will not process the request and will let the default handler process it.
Load balancers
The load balancer or upstream module forwards your requests to one of the many congured backends or upstreams. Nginx, by default, has two load-balancing modules built in: Round Robin and the IP Hash method (look at ngx_http_upstream_module). There are other third-party modules available that allow you to do load balancing based on various hashing mechanisms, for example, Consistent Hashing.
Filters
After a handler produces a response, the lters are executed. Filters do the postprocessing on the handler's response. One example can be that you need to compress the response, or add certain headers to it. Multiple lters can associate with each location.
Order of execution
The order of execution of Nginx lters is determined when they are compiled. You can see the order of the execution after compiling the code in ngx_modules.c. This le is generated on the y by the modules script, which is found at nginx/auto/. This script makes sure that it maintains the correct order of the module and lter execution.
[ 84 ]
Chapter 5
The built-in modules do need a specic order, for example, a gzip lter should run after the header and body lters have been executed. The new custom modules are generally executed in the end. Filters do not execute in a fully blocked manner, rather the output of the lters is streamed through the chain of lters. By default, one lter processes some data and passes it on to the next module and so on. The amount of data processed at a time is usually a multiple of the page size. Different modules, for example, gzip, allow you to adjust this value.
The contents of the config le will depend on what kind of module you are writing. For this simple module, it will look like the following code:
ngx_addon_name=ngx_http_hello_module HTTP_MODULES="$HTTP_MODULES ngx_http_hello_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_ module.c"
The le is quite self-explanatory. In the second line we are adding the module to a list of HTTP modules. Depending on which module type you are writing, you will need to add it to a different list. You can see the full list in the modules script found at nginx/auto/.
[ 85 ]
Before compiling, the module needs to be explicitly specied using the configure script as in the following code. The add-module list should contain a list of all third-party modules you want to include in the compilation.
./configure --add-module=path/to/your/new/module/directory
static char *ngx_http_hello(ngx_conf_t *cf, void *post, void *data); static ngx_conf_post_handler_pt ngx_http_hello_p = ngx_http_hello; /* * The structure will hold the value of the * module directive hello */ typedef struct { ngx_str_t name; } ngx_http_hello_loc_conf_t; /* The function which initializes memory for the module configuration structure */ static void * ngx_http_hello_create_loc_conf(ngx_conf_t *cf) { ngx_http_hello_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t)); if (conf == NULL) { return NULL; } return conf; } [ 86 ]
Chapter 5 /* * The command array or array, which holds one subarray for each module * directive along with a function which validates the value of the * directive and also initializes the main handler of this module */ static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, name), &ngx_http_hello_p }, ngx_null_command };
static ngx_str_t hello_string; /* * The module context has hooks , here we have a hook for creating * location configuration */ static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, NULL, NULL, NULL, /* create main configuration */ /* init main configuration */ /* create server configuration */ /* merge server configuration */
/* * The module which binds the context and commands * */ ngx_module_t ngx_http_hello_module = { NGX_MODULE_V1, [ 87 ]
Creating Your Own Module &ngx_http_hello_module_ctx, ngx_http_hello_commands, NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; /* * Main handler function of the module. */ static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; /* we response to 'GET' and 'HEAD' requests only */ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } /* discard request body, since we don't need it here */ rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } /* set the 'Content-type' header */ r->headers_out.content_type_len = sizeof("text/html") - 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; /* send the header only, if the request type is http 'HEAD' */ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = hello_string.len; /* /* /* /* /* /* /* /* /* /* module context */ module directives */ module type */ init master */ init module */ init process */ init thread */ exit thread */ exit process */ exit master */
[ 88 ]
Chapter 5 return ngx_http_send_header(r); } /* allocate a buffer for your response body */ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* attach this buffer to the buffer chain */ out.buf = b; out.next = NULL; /* adjust the pointers of the buffer */ b->pos = hello_string.data; b->last = hello_string.data + hello_string.len; b->memory = 1; /* this buffer is in memory */ b->last_buf = 1; /* this is the last buffer in the buffer chain */ /* set the status line */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = hello_string.len; /* send the headers of your response */ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } /* send the buffer chain of your response */ return ngx_http_output_filter(r, &out); } /* * Function for the directive hello , it validates its value * and copies it to a static variable to be printed later */ static char * ngx_http_hello(ngx_conf_t *cf, void *post, void *data) { ngx_http_core_loc_conf_t *clcf;
[ 89 ]
Creating Your Own Module clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_hello_handler; ngx_str_t *name = data; // i.e., first field of ngx_http_hello_loc_conf_t if (ngx_strcmp(name->data, "") == 0) { return NGX_CONF_ERROR; } hello_string.data = name->data; hello_string.len = ngx_strlen(hello_string.data); return NGX_CONF_OK; }
A sample conguration for this extended hello world module could look as follows:
server { listen 8080; server_name localhost; location / { hello 'Hello World'; } }
[ 90 ]
The members of this structure should use Nginx's special data types (ngx_uint_t, ngx_flag_t, and ngx_str_t), which are simply aliases for basic/primitive types. You can look into core/nginx_config.h in the source tree for the data type denitions. There should be as many members of this structure as the module directives. In the preceding example our module only has one directive, so we can already tell that this module will support a single directive/option at the location level, which will populate the member name of this structure. As it must be obvious by now, that the elements in the conguration structure are lled by module directives dened in the conguration le.
Module directives
After you have dened the place where the value of the module directives will be stored, it is time to dene the name of the module directives and what kind and type of arguments they will accept. A module's directives are dened in a static array of the ngx_command_t type structure. Looking at the example code we previously wrote, the following is what the directives structure looks like:
static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, name), &ngx_http_hello_p }, ngx_null_command };
The preceding structure may look a little bit complicated. However, we will now look at each one of those to understand them a little better. The rst argument denes the name of the directive. This is of type ngx_str and is instantiated with the directive name, for example, ngx_str("hello"). An ngx_ str_t data type is a struct type with data and length elements. Nginx uses this data structure for all the strings.
[ 91 ]
The second argument denes the type of the directive, and what kind of arguments it accepts. The acceptable values for these parameters should be bitwise ordered with each other. The possibilities are as follows:
NGX_HTTP_MAIN_CONF: NGX_HTTP_SRV_CONF : NGX_HTTP_LOC_CONF : section NGX_HTTP_UPS_CONF : section NGX_CONF_NOARGS : NGX_CONF_TAKE1 : NGX_CONF_TAKE2 : NGX_CONF_TAKE7 : NGX_CONF_TAKE12 : NGX_CONF_TAKE13 : NGX_CONF_TAKE23 : NGX_CONF_TAKE123 : NGX_CONF_TAKE1234 : NGX_CONF_FLAG "off" NGX_CONF_1MORE NGX_CONF_2MORE arguments directive should be used in main section directive should be used in the server section directive should be used in the location directive should be used in the upstream directive will take no arguments directive will take 1 argument directive will take 2 arguments directive directive directive directive directive directive will will will will will will take take take take take take 7 arguments 1 or 2 arguments 1 or 3 arguments 2 or 3 arguments 1, 2 or 3 arguments 1, 2 , 3 or 4 arguments
: directive accepts a boolean value from "on" or : directive requires at least one argument : directive requires at least at least two
Please see the full details in ngx_conf_file.h found in the core folder. The maximum number of arguments that a directive can take is eight (0-7) as dened in core/ngx_conf_file.h, as shown in the following code:
#define NGX_CONF_MAX_ARGS 8
In the preceding example, we only use a single element in the array, as we are providing values for a single ngx_command_t structure. The third argument is a function pointer. This is a setup function that takes the value provided for the directive in the conguration le and stores it in the appropriate element of the structure. This function can take the following three arguments: Pointer to ngx_conf_t (main, srv, or loc) structure, which contains the values of the directive in the conguration le Pointer to the target ngx_command_t structure where the value will be stored Pointer to the module's custom conguration structure (can be NULL)
[ 92 ]
Chapter 5
Nginx provides a number of functions that can be used to set the values for the built-in data types. These functions include:
ngx_conf_set_flag_slot ngx_conf_set_str_slot ngx_conf_set_str_array_slot ngx_conf_set_keyval_slot ngx_conf_set_num_slot ngx_conf_set_size_slot ngx_conf_set_off_slot ngx_conf_set_msec_slot ngx_conf_set_sec_slot ngx_conf_set_bufs_slot ngx_conf_set_enum_slot ngx_conf_set_bitmask_slot
Module authors can also pass the pointer to their own function here, if the built-in functions are not sufcient for their purpose, for example, if the string needs to be interpreted in a certain way instead of just being stored as it is. In order to specify where these built-in (or custom) functions will store the directive values, you have to specify conf and offset as the next two arguments. conf species the type of the structure where the value will be stored (main, srv, loc) and offset species which part of this conguration structure to store it in. The following is the offset of the element in the structure, that is, offsetof(ngx_http_ hello_loc_conf_t, name). The last element is often NULL, and at the moment we can choose to ignore it. The last element of the command array is ngx_null_command, which indicates the termination.
[ 93 ]
These take different arguments depending on what they're doing. The following is the structure denition, taken from http/ngx_http_config.h, so you can see the different function signatures of the callbacks:
typedef struct { ngx_int_t (*preconfiguration)(ngx_conf_t *cf); ngx_int_t (*postconfiguration)(ngx_conf_t *cf); void char void char *conf); *(*create_main_conf)(ngx_conf_t *cf); *(*init_main_conf)(ngx_conf_t *cf, void *conf); *(*create_srv_conf)(ngx_conf_t *cf); *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void
void *(*create_loc_conf)(ngx_conf_t *cf); char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;
You can set functions you don't need to NULL, and Nginx will accept it, and do the right thing. The create functions such as create main conf, create server conf, and create location conf normally just allocate memory for the structures (such as malloc()) and initialize the elements as default values. The functions such as initialize main conf, and merge with main conf provide the opportunity to override the default values.
[ 94 ]
Chapter 5
During merging, the module authors can look for duplicate denitions of elements and throw errors if there is a problem with directives provided by conguration authors in the conguration le. Most module authors just use the last two elements as such: a function to allocate memory for ngx_loc_conf (main , srv, or loc) conguration, and a function to set defaults and merge this conguration into a merged location conguration (called ngx_http_<module name >_merge_loc_conf). The following is an example module context structure:
/* * The module context has hooks , here we have a hook for creating * location configuration */ static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, /* preconfiguration */ NULL, /* 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 */ };
We can have a closer look now at these functions, which set up the location based on conguration.
create_loc_conf
The following is what a basic create_loc_conf function looks like. It takes a directive structure (ngx_conf_t) and returns a module conguration structure that is newly allocated.
/* The function which initializes memory for the module configuration structure */ static void * ngx_http_hello_create_loc_conf(ngx_conf_t *cf) { ngx_http_hello_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t)); if (conf == NULL) {
[ 95 ]
The Nginx memory allocation takes care of freeing the memory if you use the built-ins ngx_palloc (a malloc wrapper) or ngx_pcalloc (a calloc wrapper).
merge_loc_conf
The sample module we created does not contain a merge location conf function. However, we can look at the following sample code just to explain some basic concepts. You generally need a merge function if a directive can be dened multiple times. It is your job to dene a merge function that can set the appropriate value in case it is dened multiple times or in multiple locations.
static char * ngx_http_example_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_example_loc_conf_t *prev = parent; ngx_http_example_loc_conf_t *conf = child; ngx_conf_merge_uint_value(conf->val1, prev->val1, 10); ngx_conf_merge_uint_value(conf->val2, prev->val2, 20); if (conf->val1 < 1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "value 1 must be equal or more than 1"); return NGX_CONF_ERROR; } if (conf->val2 < conf->val1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "val2 must be equal or more than val1"); return NGX_CONF_ERROR; } return NGX_CONF_OK; }
Nginx provides very useful merging built-in functions for various data types (ngx_ conf_merge_<data type>_value). These functions take the arguments as follows: The location's value (can refer to an element in a structure) The value to use if the rst value is not set The default value if both rst and second values are not set
[ 96 ]
Chapter 5
The rst argument stores the result. See core/ngx_conf_file.h for a full list of available merge functions. The following is an extract from the le:
#define ngx_conf_merge_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET) { \ conf = (prev == NGX_CONF_UNSET) ? default : prev; \ } #define ngx_conf_merge_ptr_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET_PTR) { \ conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev; \ } #define ngx_conf_merge_uint_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET_UINT) { \ conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; \ } #define ngx_conf_merge_msec_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET_MSEC) { \ conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev; \ } #define ngx_conf_merge_sec_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET) { \ conf = (prev == NGX_CONF_UNSET) ? default : prev; \ } #define ngx_conf_merge_size_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET_SIZE) { \ conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; \ } #define ngx_conf_merge_off_value(conf, prev, default) \ if (conf == NGX_CONF_UNSET) { \ conf = (prev == NGX_CONF_UNSET) ? default : prev; \ } #define ngx_conf_merge_str_value(conf, prev, default) \ if (conf.data == NULL) { \ if (prev.data) { \ conf.len = prev.len; \ conf.data = prev.data; \ } else { \ conf.len = sizeof(default) - 1; \ conf.data = (u_char *) default; \ } \ } #define ngx_conf_merge_bufs_value(conf, prev, default_num, [ 97 ]
Creating Your Own Module default_size) \ if (conf.num == 0) { \ if (prev.num) { \ conf.num = prev.num; \ conf.size = prev.size; \ } else { \ conf.num = default_num; \ conf.size = default_size; \ } \ }
As you can see these functions are dened as macros, and they are expanded and placed inline in the code during compilation. Another thing to learn is how to log errors. The function outputs to the log le using the ngx_conf_log_error functionwhere you specify a log leveland returns NGX_CONF_ERROR, which stops server startup. There are several log levels dened in Nginx. These are dened in ngx_log.h. The following is an extract from the code:
#define #define #define #define #define #define #define #define #define NGX_LOG_STDERR NGX_LOG_EMERG NGX_LOG_ALERT NGX_LOG_CRIT NGX_LOG_ERR NGX_LOG_WARN NGX_LOG_NOTICE NGX_LOG_INFO NGX_LOG_DEBUG 0 1 2 3 4 5 6 7 8
Chapter 5 ngx_uint_t ngx_uint_t ngx_uint_t ngx_uint_t void ngx_command_t ngx_uint_t ngx_int_t ngx_int_t ngx_int_t ngx_int_t void void void uintptr_t uintptr_t uintptr_t uintptr_t uintptr_t uintptr_t uintptr_t uintptr_t }; spare1; spare2; spare3; version; *ctx; *commands; type; (*init_master)(ngx_log_t *log); (*init_module)(ngx_cycle_t *cycle); (*init_process)(ngx_cycle_t *cycle); (*init_thread)(ngx_cycle_t *cycle); (*exit_thread)(ngx_cycle_t *cycle); (*exit_process)(ngx_cycle_t *cycle); (*exit_master)(ngx_cycle_t *cycle); spare_hook0; spare_hook1; spare_hook2; spare_hook3; spare_hook4; spare_hook5; spare_hook6; spare_hook7;
You can see that the macros NGX_MODULE_V1 and NGX_MODULE_V1_PADDING provide the values for the structure elements before and after the highlighted section in the preceding code. This is a detail we don't need to get into at the moment. For now, look at the following example on how to use them:
/* * The module which binds the context and commands * */ 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 }; [ 99 ]
You can see from the comments in the preceding code what each argument means. The rst and last elements are the masks that hide the additional structure elements mainly because we don't need them, and they are place holders for the future. We also provide a module type, which in this case is HTTP. Most of the user-dened custom modules will be of this type. You can dene other types such as CORE, MAIL, EVENT and so on; however, they are mostly not used as add-on module types.
Chapter 5 if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* attach this buffer to the buffer chain */ out.buf = b; out.next = NULL; /* adjust the pointers of the buffer */ b->pos = hello_string.data; b->last = hello_string.data + hello_string.len; b->memory = 1; /* this buffer is in memory */ b->last_buf = 1; /* this is the last buffer in the buffer chain */ /* set the status line */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = hello_string.len; /* send the headers of your response */ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } /* send the buffer chain of your response */ return ngx_http_output_filter(r, &out); }
There are a few things to learn in the code. As explained earlier, this module basically prints whatever you had provided in the conguration. For example, according to the following conguration, this module will make sure that it prints Hello World whenever you open http://localhost:8080:
server { listen 8080; server_name localhost; location / { hello 'Hello World'; } }
[ 101 ]
This method receives the HTTP request as an argument. If your module only responds to a certain type of HTTP requests, you can check by looking at the HTTP request structure. For example, our module only responds to HTTP GET and HEAD requests as checked by this chunk of code; otherwise it returns "error code 405 (not allowed)". All the HTTP error codes are dened in ngx_http_request.h as follows:
/* we response to 'GET' and 'HEAD' requests only */ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; }
Next, we discard the request body as in this module we don't need it. In several modules, one will write a body that will be important, however, right now we don't care about it. By discarding the request body, Nginx will not read the request body fully for processing and will not allocate memory for it internally. Next we set some HTTP headers in our response. All headers you can set in the response can be accessed through the headers_out member of the HTTP request structure. The headers_out structure allows you to set a number of outgoing headers. The extract from ngx_http_request.h is as follows:
typedef struct { ngx_list_t ngx_uint_t ngx_str_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_table_elt_t ngx_str_t size_t ngx_str_t [ 102 ] headers; status; status_line; *server; *date; *content_length; *content_encoding; *location; *refresh; *last_modified; *content_range; *accept_ranges; *www_authenticate; *expires; *etag; *override_charset; content_type_len; content_type;
Chapter 5 ngx_str_t u_char ngx_uint_t ngx_array_t off_t time_t time_t } ngx_http_headers_out_t; charset; *content_type_lowcase; content_type_hash; cache_control; content_length_n; date_time; last_modified_time;
The next important step in our module is allocating memory for the response buffer. This memory should be allocated using Nginx's own APIs as mentioned in earlier chapters (since it also automatically takes care of freeing it). This can be done because the memory is allocated from a local memory pool, so that all memory allocations are tracked. The response is created in a linked list or chain of buffers, each of which is of the size of ngx_buf_s. This allows Nginx to process the response in a parallel way. If there are other handlers or lters that need to postprocess the response, they can start their work as soon as the rst buffer in the chain is ready, while you are lling up the second buffer. This allows Nginx to keep operating in a parallel fashion without waiting for any module to completely nish processing rst. When you are nished with creating the response in the last buffer, you should set b->last_buf = 1. This, as it is obvious from the name, will tell Nginx that this is the last response buffer from your module. If the response processing was successful, you would want to set the status of the response header to HTTP_OK. This is done by r->headers_out.status = NGX_ HTTP_OK. You will then need to initiate the chain of header lters by calling ngx_http_ send_header. This will indicate to Nginx that processing of the output headers has nished, and now Nginx can pass them to a chain of lters, which might want to do further postprocessing to the headers. The nal step is returning from the function by calling ngx_http_output_filter. This will initiate the process of the HTTP body lter chain. That is, Nginx or custom lter modules that might have been installed to do postprocessing on the HTTP response body you have just created in the buffer.
[ 103 ]
The summary of creating the Nginx custom module can be as follows: 1. Create a module conguration that is structured either for location , main, or server; each with a specic naming convention (see ngx_http_hello_ loc_conf_t). The allowed directives of the module are in a static array of typengx_ command_t (see ngx_http_hello_commands). This will also have the
function's pointers that will have the code to validate the value of each directive as well as initialize the handler.
2. Create a module context struct such as ngx_http_<module name>_module_ ctx of type ngx_http_module_t which has a bunch of hooks for setting up conguration. Here you can have the post conguration hook, for example, to set up the main handler of your module (see ngx_http_hello_module_ctx). 3. Then we do the module denition, which is also a struct of type ngx_module_t and contains references to the module context and module commands that you created in the previous steps (see ngx_http_hello_module). 4. Create the main module handler function that processes the HTTP request. This function also outputs the response headers and body in a series of xed size buffers.
Chapter 5
Summary
In this chapter we have learned the process of creating a simple Nginx handler module. We also looked at which basic structures a new module should dene, and how to link them to each other. Finally, we looked at a small handler function that does a basic task, but provides you the basis of writing a much more complicated module. If you are an Nginx module developer, you must extensively browse other modules and Nginx source code, which will help you learn how to do different things within your code and which API to use in general. You will also nd Nginx Development Kit at https://github.com/simpl/ ngx_devel_kit. This will provide you additional conf_set functions for regexes, complex/script values, paths, and macros to simplify tasks such as checking for NULL values when doing ngx_array_push and much more, which will simplify your life while writing custom Nginx modules.
[ 105 ]
Alternatively, you can buy the book from Amazon, BN.com, Computer Manuals and most internet book retailers.
www.PacktPub.com