The extended "Hello world" module

Now we will proceed towards creating a simple Nginx module. This module will print a configurable text in your browser whenever you enter a specific location. This is a very simple module and the idea is to just introduce the core concepts of how to create an Nginx module. This is based on and is an enhanced version of the simple Hello world module found at http://blog.zhuzhaoyuan.com/2009/08/creating-a-hello-world-nginx-module/. This module is an example of a handler module.

Writing and compiling a module

The first thing you have to do is to obviously create a folder for your new module. Create it anywhere other than the Nginx source tree. You should create the following two files to start with:

  • config
  • ngx_http_hello_module.c

The contents of the config file 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 file 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/.

Before compiling, the module needs to be explicitly specified 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

This has to be followed by make and make install as usual.

The "Hello world" source code

The following code is from ngx_http_hello_module.c:

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>


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


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

/*
 * 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;
    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;
  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 ofngx_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 configuration for this extended hello world module could look as follows:

server {
listen 8080;
server_name localhost;

location / {
hello 'Hello World';
  }
}

Components of the Nginx module

There are many components on an Nginx module depending on the type of the module. We will now discuss those parts that are common to almost all the modules. The intention is to present to you a reference in an easy to understand way so that you can be ready to write your own module.

Module configuration structures

Modules can define one configuration for each of the configuration file's configuration contexts—there is an individual structure for the main, server, and location contexts. It is OK for most modules to simply have a location structure. These structures should be named as convention ngx_http_<module name>_(main|srv|loc)_conf_t. The following is the code snippet from the sample module:

typedef struct {
  ngx_str_t   name;
} ngx_http_hello_loc_conf_t;

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 definitions.

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 configuration structure are filled by module directives defined in the configuration file.

Module directives

After you have defined the place where the value of the module directives will be stored, it is time to define the name of the module directives and what kind and type of arguments they will accept. A module's directives are defined 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 first argument defines 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.

The second argument defines 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: directive should be used in main section
NGX_HTTP_SRV_CONF : directive should be used in the server section
NGX_HTTP_LOC_CONF : directive should be used in the locationsection
NGX_HTTP_UPS_CONF : directive should be used in the upstreamsection
NGX_CONF_NOARGS   : directive will take no arguments
NGX_CONF_TAKE1    : directive will take 1 argument
NGX_CONF_TAKE2    : directive will take 2 arguments
…
NGX_CONF_TAKE7    : directive will take 7 arguments
NGX_CONF_TAKE12   : directive will take 1 or 2 arguments
NGX_CONF_TAKE13   : directive will take 1 or 3 arguments   
NGX_CONF_TAKE23   : directive will take 2 or 3 arguments
NGX_CONF_TAKE123  : directive will take 1, 2 or 3 arguments
NGX_CONF_TAKE1234 : directive will take 1, 2 , 3 or 4 arguments

NGX_CONF_FLAG     : directive accepts a boolean value from "on" or"off"
NGX_CONF_1MORE    : directive requires at least one argument
NGX_CONF_2MORE    : directive requires at least at least twoarguments

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 defined 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 configuration file 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 configuration file
  • Pointer to the target ngx_command_t structure where the value will be stored
  • Pointer to the module's custom configuration structure (can be NULL)

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

Some of these are described as follows:

  • ngx_conf_set_flag_slot: This translates on or off to 1 or 0
  • ngx_conf_set_str_slot: This saves a string as ngx_str_t
  • ngx_conf_set_num_slot: This parses a number and saves it to an integer
  • ngx_conf_set_size_slot: This parses a data size (5k, 2m, and so on) and saves it to size_t

Module authors can also pass the pointer to their own function here, if the built-in functions are not sufficient 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 specifies the type of the structure where the value will be stored (main, srv, loc) and offset specifies which part of this configuration 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.

The module context

The third structure in an Nginx module that needs to be defined is a static ngx_http_module_t structure, which just has the function pointers for creating the main, srv, and loc configurations, and merging them together. Its name is ngx_http_<module name>_module_ctx. The function references that you can provide are as follows:

  • Pre configuration
  • Post configuration
  • Creating the main conf
  • Initializing the main conf
  • Creating the server conf
  • Merging it with the main conf
  • Creating the location conf
  • Merging it with the server conf

These take different arguments depending on what they're doing. The following is the structure definition, 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       *(*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;

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.

During merging, the module authors can look for duplicate definitions of elements and throw errors if there is a problem with directives provided by configuration authors in the configuration file.

Most module authors just use the last two elements as such: a function to allocate memory for ngx_loc_conf (main , srv, or loc) configuration, and a function to set defaults and merge this configuration into a merged location configuration (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 configuration.

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 configuration 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) {
    return NULL;
  }

  return conf;
}

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 defined multiple times. It is your job to define a merge function that can set the appropriate value in case it is defined 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 first value is not set
  • The default value if both first and second values are not set

The first 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 file:

#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,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 defined 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 file using the ngx_conf_log_error function—where you specify a log level—and returns NGX_CONF_ERROR, which stops server startup.

There are several log levels defined in Nginx. These are defined in ngx_log.h. The following is an extract from the code:

#define NGX_LOG_STDERR            0
#define NGX_LOG_EMERG             1
#define NGX_LOG_ALERT             2
#define NGX_LOG_CRIT              3
#define NGX_LOG_ERR               4
#define NGX_LOG_WARN              5
#define NGX_LOG_NOTICE            6
#define NGX_LOG_INFO              7
#define NGX_LOG_DEBUG             8

The module definition

The next structure a new module should define is the module definition structure or the ngx_module_t structure. The variable is called ngx_http_<module name>_module. This structure binds together the structures we have been defining until now. You have to provide the pointers to the context and directives structures, as well as the remaining callbacks (exit thread, exit process, and so on). The module definition can act like a key to look up data associated with a particular module. The module definition of our custom module looks as follows:

#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

struct ngx_module_s {
  ngx_uint_t            ctx_index;
  ngx_uint_t            index;

  ngx_uint_t            spare0;
  ngx_uint_t            spare1;
  ngx_uint_t            spare2;
  ngx_uint_t            spare3;
  ngx_uint_t            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;
};

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

You can see from the comments in the preceding code what each argument means. The first 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-defined custom modules will be of this type. You can define other types such as CORE, MAIL, EVENT and so on; however, they are mostly not used as add-on module types.

The handler function

The final piece of the puzzle after all the preparation work and configuration structures is the actual handler function which does all the work. The handler function for our sample module is as follows:

/*
 * Main handler function of the module.
 */
static ngx_int_tngx_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.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;

  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);
}

There are a few things to learn in the code. As explained earlier, this module basically prints whatever you had provided in the configuration. For example, according to the following configuration, 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';
  }
}

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 defined 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                        headers;

  ngx_uint_t                        status;
  ngx_str_t                         status_line;

  ngx_table_elt_t                  *server;
  ngx_table_elt_t                  *date;
  ngx_table_elt_t                  *content_length;
  ngx_table_elt_t                  *content_encoding;
  ngx_table_elt_t                  *location;
  ngx_table_elt_t                  *refresh;
  ngx_table_elt_t                  *last_modified;
  ngx_table_elt_t                  *content_range;
  ngx_table_elt_t                  *accept_ranges;
  ngx_table_elt_t                  *www_authenticate;
  ngx_table_elt_t                  *expires;
  ngx_table_elt_t                  *etag;

  ngx_str_t                        *override_charset;

  size_t                            content_type_len;
  ngx_str_t                         content_type;
  ngx_str_t                         charset;
  u_char                           *content_type_lowcase;
  ngx_uint_t                        content_type_hash;

  ngx_array_t                       cache_control;

  off_t                             content_length_n;
  time_t                            date_time;
  time_t                            last_modified_time;
} ngx_http_headers_out_t;

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 filters that need to postprocess the response, they can start their work as soon as the first buffer in the chain is ready, while you are filling up the second buffer. This allows Nginx to keep operating in a parallel fashion without waiting for any module to completely finish processing first.

When you are finished 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 filters by calling ngx_http_send_header. This will indicate to Nginx that processing of the output headers has finished, and now Nginx can pass them to a chain of filters, which might want to do further postprocessing to the headers.

The final step is returning from the function by calling ngx_http_output_filter. This will initiate the process of the HTTP body filter chain. That is, Nginx or custom filter modules that might have been installed to do postprocessing on the HTTP response body you have just created in the buffer.

The summary of creating the Nginx custom module can be as follows:

  1. Create a module configuration that is structured either for location , main, or server; each with a specific 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 configuration. Here you can have the post configuration hook, for example, to set up the main handler of your module (see ngx_http_hello_module_ctx).
  3. Then we do the module definition, 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 fixed size buffers.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.36