Apache's plug-ins exist in the form of dynamic libraries and are dynamically loaded through configuration; nginx plug-ins need to be compiled into nginx executable programs, and the same is true for openresty, which directly embeds the lua interpreter into nginx, making it capable of parsing lua scripts. The nginx built-in plugin directory is under src/http/modules.
The process of writing nginx plug-ins is complicated and requires a certain understanding of nginx source code and data structure. nginx implements common functions such as strings, timers, containers (HashTable, Queue, red-black tree), memory pools, logs, network processing, threads, etc. The learning value is very high.
This article is from the Taobao Tengine document, and some comments are made according to my own understanding.
1. hello world module
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> // hello configuration structure typedef struct { ngx_int_t hello_counter; // counter configuration } ngx_http_hello_loc_conf_t; // context callback function static ngx_int_t ngx_http_hello_init (ngx_conf_t * cf); static void *ngx_http_hello_create_loc_conf( ngx_conf_t *cf ); // configure the handler function static char * ngx_hello_set (ngx_conf_t *, ngx_command_t *, void *); // Callback static ngx_int_t ngx_http_hello_handler( ngx_http_request_t * ); // configuration item static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_counter"), // configuration name hello_counter NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, // Configure as bool type, the value is on/off ngx_hello_set, // configuration handler NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command // null terminated }; // module context static ngx_http_module_t ngx_hello_ctx = { NULL, ngx_http_hello_init, // Called after reading the module configuration NULL, NULL, NULL, NULL, ngx_http_hello_create_loc_conf, // Called after reading the location configuration (create one for each location) NULL }; // module definition ngx_module_t ngx_http_hello_module = { NGX_MODULE_V1, &ngx_hello_ctx, ngx_http_hello_commands, NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; static char * ngx_hello_set (ngx_conf_t * cf, ngx_command_t * cmd, void * conf) { ngx_http_hello_loc_conf_t *local_conf; local_conf = conf; char *rv = NULL; // Read NGX_CONF_FLAG type parameters rv = ngx_conf_set_flag_slot(cf, cmd, conf); ngx_conf_log_error( NGX_LOG_INFO, cf, 0, "hello_counter:%d", local_conf->hello_counter ); return rv; } static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf) { ngx_http_hello_loc_conf_t* local_conf = NULL; local_conf = ngx_pcalloc (cf-> pool, sizeof (ngx_http_hello_loc_conf_t)); if ( local_conf == NULL ) { return NULL; } // Initially set default value local_conf-> hello_counter = NGX_CONF_UNSET; return local_conf; } 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; } // Set the callback function in the NGX_HTTP_CONTENT_PHASE phase *h = ngx_http_hello_handler; return NGX_OK; } static int ngx_hello_visited_times = 0; // number of visits 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; ngx_http_hello_loc_conf_t *my_conf; ngx_uint_t content_length = 0; u_char ngx_hello_string[1024] = {0}; ngx_log_error( NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!" ); // get configuration value my_conf = ngx_http_get_module_loc_conf( r, ngx_http_hello_module ); if ( my_conf->hello_counter == NGX_CONF_UNSET || my_conf->hello_counter == 0 ) { ngx_sprintf( ngx_hello_string, "<h1>Non counter</h1>" ); } else { ngx_sprintf (ngx_hello_string, "<h1>Visited Times:%d</h1>", ++ngx_hello_visited_times ); } ngx_log_error( NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string ); content_length = ngx_strlen (ngx_hello_string); // allocate response buffer b = ngx_pcalloc (r-> pool, sizeof (ngx_buf_t)); out.buf = b; // attach out.next = NULL; b->pos = ngx_hello_string; b->last = ngx_hello_string + content_length; b->memory = 1; b->last_buf = 1; // set the response ngx_str_set( &r->headers_out.content_type, "text/html" ); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = content_length; // send response rc = ngx_http_send_header( r ); // pass to other filters for processing return ngx_http_output_filter( r, &out ); }
2. Compile
Plugin directory:
~/hlmodule
-- config
-- ngx_http_hello_module.c
Prepare the config file with the following contents:
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"
Compile:
./configure --prefix=~/nginx --add-module=~/hlmodule make && make install
3. Configure to enable hello_counter
vi conf/nginx.conf
location /hello { hello_counter on; // enable counting }
4. Test
hello_counter is set on:
hello_counter is set off:
Reference link:
Taobao nginx module development
http://tengine.taobao.org/book/chapter_03.html#hello-handler
official reference
http://nginx.org/en/docs/dev/development_guide.html#core_modules