Nginx module development: handler module implementation

1, Functions to be implemented by the module

Implementation function: count the access times of each ip address and display the corresponding html. (what's the use of this function? It can be made into a black-and-white list according to the statistics of the number of times. For those with more visits, access is prohibited)

In the conf file, you can set the command count to configure the module. When accessing the / test resource, the current ip access times will be counted.

As shown in the figure below, the accessed resource is 102.168.232.137 / test. Jump to 192.168.232.1 and count will be + 1 every time you access it

When accessing different IPs, the corresponding count will restart. Statistics based on client IP

1. Shared memory and slab

To make every access, the count is increased by one, but the worker process of the nginx server accessed by the browser may not be the same. Therefore, the way of shared memory can be used for inter process communication, and the number of accesses is shared resources.
Then, the shared memory can be managed and allocated through the slab allocation strategy.

What is the difference between shmem and slab?
shmem is shared memory and slab is memory allocation strategy. Therefore, the allocation strategy of slab can be adopted for shared memory

2. Stored data structure

You can see that all ip addresses and their corresponding access times are shown below

How to realize the above functions?
The ip address is used as the key and the azimuth count is used as the value, which is organized through the red black tree. During each access, just make count+1 corresponding to the ip. If the key of the ip is not in the red black tree, create a new node and assign count=1.
To display the above content, you only need to traverse the red black tree, then encode it into html format and send it out.

Because it is returned directly on nginx side, there is no reverse proxy to the server. Therefore, it belongs to the handler module.

2, Implement the handler module

The following customized functions are executed in the following order:
1.ngx_http_pagecount_create_location_conf allocates a piece of memory for the structure to pass parameters
2.ngx_http_pagecount_set initializes a shared memory (used to store red and black trees) and defines a handler function (execute count+1 and return html)
3.ngx_http_pagecount_init returns NGX directly_ OK

tatic ngx_command_t count_commands[] = {
	{
		ngx_string("count"),//The custom http module instruction is count
		NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,//Flag bit: the instruction configuration location is http{location {...}}, And the command requires no parameters
		ngx_http_pagecount_set,//The execution function when parsing the count command (when parsing the conf file, it will be executed when encountering the count command)
		NGX_HTTP_LOC_CONF_OFFSET,//Instruction storage address. The instruction address is obtained through the function of finding the instruction address. The storage location of the instruction is http{location {..}}
		0, NULL
	},
	ngx_null_command//Instruction array end flag
};

static ngx_http_module_t count_ctx = {
	NULL,
	ngx_http_pagecount_init,//After parsing the conf, you can initialize http_pagecount
	
	NULL,
	NULL,

	NULL,
	NULL,

	ngx_http_pagecount_create_location_conf,//Create a piece of memory to place the configuration information of loc
	NULL,
};

//ngx_http_count_module 
ngx_module_t ngx_http_pagecount_module = {  //The name of the module must be unique and cannot conflict with the names of other modules
	NGX_MODULE_V1,
	&count_ctx,//Specific module http module
	count_commands,//Module supported instructions
	NGX_HTTP_MODULE,//The module type is http module
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
};

typedef struct {
	int count; //count
} ngx_http_pagecount_node_t;

1,ngx_http_pagecount_create_location_conf

Allocate a space for a defined structure to store information such as shared memory

typedef struct
{
    ssize_t shmsize;
 
    ngx_slab_pool_t *shpool;
 
    ngx_http_pagecount_shm_t *sh;
} ngx_http_pagecount_conf_t;

void  *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf) {

	ngx_http_pagecount_conf_t *conf;
	
	
	conf = ngx_palloc(cf->pool, sizeof(ngx_http_pagecount_conf_t));
	if (NULL == conf) {
		return NULL;
	}

	conf->shmsize = 0;
	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_create_location_conf");

	// init conf data
	// ... 

	return conf;

}

2,ngx_http_pagecount_set

Get a piece of shared memory, set the shared memory initialization function, and set the handler function
When nginx starts, the shared memory will be initialized
The handler function is called once for each visit

static char *ngx_http_pagecount_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {

	ngx_shm_zone_t *shm_zone;//An area of shared memory (not the entire shared memory)
	ngx_str_t name = ngx_string("pagecount_slab_shm");

	ngx_http_pagecount_conf_t *mconf = (ngx_http_pagecount_conf_t*)conf;
	ngx_http_core_loc_conf_t *corecf;
	
	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_set000");
	
	mconf->shmsize = 1024*1024;
	
	shm_zone = ngx_shared_memory_add(cf, &name, mconf->shmsize, &ngx_http_pagecount_module);//Take one piece out of the shared area (why is it called add here? It means that one piece is added to what has been used outside)
	if (NULL == shm_zone) {
		return NGX_CONF_ERROR;
	}

	
	shm_zone->init = ngx_http_pagecount_shm_init;//Initialization function of shared memory (this memory is made into slab here)
	shm_zone->data = mconf;

	corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	corecf->handler = ngx_http_pagecount_handler;//This function (i.e. accounting number) is executed every time the web page is visited

	return NGX_CONF_OK;
}

1)ngx_http_pagecount_shm_init

Initialize shared memory

Here, we mainly configure the slab and use the shared memory allocated by the slab to initialize a red black tree

ngx_int_t ngx_http_pagecount_shm_init (ngx_shm_zone_t *zone, void *data) {

	ngx_http_pagecount_conf_t *conf;
	ngx_http_pagecount_conf_t *oconf = data;

	conf = (ngx_http_pagecount_conf_t*)zone->data;
	if (oconf) {
		conf->sh = oconf->sh;
		conf->shpool = oconf->shpool;
		return NGX_OK;
	}

	printf("ngx_http_pagecount_shm_init 0000\n");
	
	conf->shpool = (ngx_slab_pool_t*)zone->shm.addr;
	conf->sh = ngx_slab_alloc(conf->shpool, sizeof(ngx_http_pagecount_shm_t));//Allocate a piece of space through the slab (that is, conf - > SH is the shared memory in the slab)
	if (conf->sh == NULL) {
		return NGX_ERROR;
	}

	conf->shpool->data = conf->sh;

	printf("ngx_http_pagecount_shm_init 1111\n");
	
	ngx_rbtree_init(&conf->sh->rbtree, &conf->sh->sentinel, //The red black tree initialization in ngx needs to be inserted by itself (how to compare key s is the same as the part in the insertion timer, just imitate it)
		ngx_http_pagecount_rbtree_insert_value);


	return NGX_OK;

}

2)ngx_http_pagecount_handler

handler function. After loading the module under the count command and accessing the specified resources, this function will be called
It mainly refers to the number of accesses corresponding to the ip + 1, and traverses the red black tree to obtain all ip and corresponding times in the way of htmp coding

//handler is a direct return structure after receiving a request. It doesn't need to be like
static ngx_int_t ngx_http_pagecount_handler(ngx_http_request_t *r) {


	u_char html[1024] = {0};//It's best to allocate memory pool
	int len = sizeof(html);
	
	ngx_rbtree_key_t key = 0;


	struct sockaddr_in *client_addr =  (struct sockaddr_in*)r->connection->sockaddr;//Get the address of the client
	

	ngx_http_pagecount_conf_t *conf = ngx_http_get_module_loc_conf(r, ngx_http_pagecount_module);
	key = (ngx_rbtree_key_t)client_addr->sin_addr.s_addr;//ip address

	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_handler --> %x\n", key);

	ngx_shmtx_lock(&conf->shpool->mutex);//Critical resource locking
	ngx_http_pagecount_lookup(r, conf, key);//If the corresponding ip(key) is found, the number of accesses (value) + +. If it is not found, a new node is inserted, and the number of accesses is 1	
	ngx_shmtx_unlock(&conf->shpool->mutex);
	
	ngx_encode_http_page_rb(conf, (char*)html);//Traverse the red black tree to generate the string format of html

	//The rest is to encode the header and body

	//header
	r->headers_out.status = 200;
	ngx_str_set(&r->headers_out.content_type, "text/html");
	ngx_http_send_header(r);

	//body
	ngx_buf_t *b = ngx_pcalloc(r->pool,  sizeof(ngx_buf_t));

	ngx_chain_t out;
	out.buf = b;
	out.next = NULL;

	b->pos = html;
	b->last = html+len;
	b->memory = 1;
	b->last_buf = 1;

	return ngx_http_output_filter(r, &out);
	
	
}

3) Some operations on red black tree

The following are some operations of red black tree, such as inserting nodes, finding nodes and traversing red black tree

static void
ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,
        ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
   ngx_rbtree_node_t **p;
   //ngx_http_testslab_node_t *lrn, *lrnt;
 
    for (;;)
    {
        if (node->key < temp->key)
        {
            p = &temp->left;
        }
        else if (node->key > temp->key) {
           	p = &temp->right;
        }
        else
        {
          	return ;
        }
 
        if (*p == sentinel)
        {
            break;
        }
 
        temp = *p;
    }
 
    *p = node;
 
    node->parent = temp;
    node->left = sentinel;
    node->right = sentinel;
    ngx_rbt_red(node);
}
static ngx_int_t ngx_http_pagecount_lookup(ngx_http_request_t *r, ngx_http_pagecount_conf_t *conf, ngx_uint_t key) {

	ngx_rbtree_node_t *node, *sentinel;

	node = conf->sh->rbtree.root;
	sentinel = conf->sh->rbtree.sentinel;

	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 111 --> %x\n", key);
	//Find the node. If it is found, let it the value (Times) corresponding to the key(ip)++
	while (node != sentinel) {

		if (key < node->key) {
			node = node->left;
			continue;
		} else if (key > node->key) {
			node = node->right;
			continue;
		} else { // key == node
			node->data ++;
			return NGX_OK;
		}
	}
	
	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 222 --> %x\n", key);

	//If no node is found, insert a node so that value = 1
	// insert rbtree
	
	
	node = ngx_slab_alloc_locked(conf->shpool, sizeof(ngx_rbtree_node_t));//Allocate to shared memory through slab
	if (NULL == node) {
		return NGX_ERROR;
	}

	node->key = key;
	node->data = 1;

	ngx_rbtree_insert(&conf->sh->rbtree, node);

	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " insert success\n");
	

	return NGX_OK;
}


static int ngx_encode_http_page_rb(ngx_http_pagecount_conf_t *conf, char *html) {

	sprintf(html, "<h1>Source Insight </h1>");
	strcat(html, "<h2>");

	//ngx_rbtree_traversal(&ngx_pv_tree, ngx_pv_tree.root, ngx_http_count_rbtree_iterator, html);

	//Traversal node
	ngx_rbtree_node_t *node = ngx_rbtree_min(conf->sh->rbtree.root, conf->sh->rbtree.sentinel);//Find the node in the lower left corner

	do {

		char str[INET_ADDRSTRLEN] = {0};
		char buffer[128] = {0};

		sprintf(buffer, "req from : %s, count: %d <br/>",
			inet_ntop(AF_INET, &node->key, str, sizeof(str)), node->data);//Convert int type key to ip address

		strcat(html, buffer);//Character connection

		node = ngx_rbtree_next(&conf->sh->rbtree, node);//Find next node

	} while (node);
	

	strcat(html, "</h2>");

	return NGX_OK;
}

3,ngx_http_pagecount_init

Null implementation, directly return an NGX_OK

ngx_int_t   ngx_http_pagecount_init(ngx_conf_t *cf) {
#if 0
	ngx_http_core_loc_conf_t *cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

	ngx_http_handler_pt *h = ngx_array_push(cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
	if (NULL == h) {
		return NGX_ERROR;
	}
	*h = ngx_http_pagecount_handler;
#elif 0

	ngx_log_error(NGX_LOG_NOTICE, cf->log, ngx_errno, "ngx_http_pagecount_init");

	ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	corecf->handler = ngx_http_pagecount_handler;

	//return NGX_OK;
#endif

	return NGX_OK;
}

Tags: C Nginx server

Posted by social_experiment on Sun, 22 May 2022 01:31:06 +0300