[Nginx] develops an HTTP filter module.

Source: Internet
Author: User
Unlike the HTTP processing module, the HTTP Filtering module processes the HTTP Response sent to users. A response returned by the server can be processed by any number of HTTP filter modules in the pipeline. The HTTP response is divided into the header and the package body. The ngx_http_send_header and ngx_http_output_filter functions are respectively responsible for sending the header and the package body. They call each filter module to process the sent response in turn.
The HTTP filter module can process the response header or package body separately or simultaneously. The prototype of the method used to process the header and the package body is as follows. They are defined in the HTTP framework module ngx_http_core_module.h:
// The method prototype typedef ngx_int_t (* ngx_http_output_header_filter_pt) (ngx_http_request_t * r) that the filter module processes the HTTP packet body method prototype typedef ngx_int_t (* optional) (ngx_http_request_t * r, ngx_chain_t * chain );


The above is the declaration of two function pointers. The HTTP filter module concatenates these pointers into a single-chain table. Each HTTP filter module defines at least one of the above function pointers. The linked list of entries is as follows:
// Extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;


When the HTTP module calls ngx_http_send_header to send the header, it traverses all HTTP header filtering modules and processes them from the module pointed to by ngx_http_top_header_filter. When the HTTP module calls ngx_http_output_filter to send the package body, the ngx_http_top_body_filter module traverses all HTTP packet body filtering modules and processes them. The code of the ngx_http_send_header function is as follows:
Aggregate (ngx_http_request_t * r) {If (R-> header_sent) {ngx_log_error (ngx_log_alert, R-> connection-> log, 0, "header already sent"); Return ngx_error ;} if (R-> err_status) {r-> headers_out.status = r-> err_status; r-> headers_out.status_line.len = 0 ;} // traverse the HTTP header filter module from the beginning. Return ngx_http_top_header_filter (r );}


The code for the ngx_http_top_header_filter function is as follows:
Except (ngx_http_request_t * r, ngx_chain_t * In) {ngx_int_t RC; ngx_connection_t * C; C = r-> connection; ngx_log_debug2 (ngx_log_debug_http, C-> log, 0, "HTTP output filter \" % v? % V \ "", & R-> Uri, & R-> ARGs); // traverses the HTTP packet body Filtering module rc = ngx_http_top_body_filter (R, In ); if (rc = ngx_error) {/* ngx_error may be returned by any filter */C-> error = 1;} return RC ;}


Each HTTP filter module contains at least one of the above two function pointers. The declaration is as follows:
static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


The method for adding an HTTP filter module to a single-chain table is as follows:
Static forward (ngx_conf_t * Cf) {// Add ngx_http_next_header_filter = ngx_http_top_header_filter; Forward = forward; // Add forward = forward; Forward = forward; return ngx_ OK from the header ;}


When this module is initialized, call the above function to add itself to the head of the linked list. When Will initialization functions like ngx_http_addition_filter_init be called? The answer is based on which member of the ngx_http_module_t struct is put in this method. In general, most official HTTP filter modules are usually placed in the ngx_http_module_t.postconfiguration function pointer, and are called back after reading all the configuration items. What is the initialization sequence of each module? This is determined by the order of the ngx_modules array in the ngx_modules.c file generated by the configure command. The front module in the array is initialized first. Because the filter module inserts itself into the head of the linked list, the sorting order of the filter module in the ngx_modules array is different from that in the actual execution order. The order of the ngx_modules array is determined by other scripts.
The following describes the process of developing a simple HTTP filter module. First, two struct types are defined:
Typedef struct {ngx_flag_t enable; // save on or off} ngx_http_myfilter_conf_t; typedef struct {ngx_int_t add_prefix;} ngx_http_myfilter_ctx_t; // HTTP context struct


Ngx_http_myfilter_conf_t is used to save the configuration item that indicates whether the filtering function is enabled. Here, we use the default configuration item Parsing Method to parse the configuration item. Ngx_http_myfilter_ctx_t is used to save the context of an HTTP request, because the processing process of the HTTP packet body is not completed at one time, that is, the function for processing the packet body will be called multiple times, therefore, we need a flag to record whether the current process has been performed by the filter module. The HTTP context is equivalent to a table that records the current processing records of the request. In this way, when a request is split into multiple processes, the same processing function will be able to know where the request has been executed and then process the current progress.
The HTTP module structure ngx_http_module_t is defined as follows:
static ngx_http_module_t  ngx_http_myfilter_module_ctx ={    NULL,                             /* preconfiguration */    ngx_http_myfilter_init,           /* postconfiguration */     NULL,                             /* create_main_conf */    NULL,                             /* init_main_conf */     NULL,                             /* create_srv_conf */    NULL,                             /* merge_srv_conf */     ngx_http_myfilter_create_conf,    /* create_loc_conf */    ngx_http_myfilter_merge_conf      /* merge_loc_conf */};


The functions of the three functions are as follows:
  • Ngx_http_myfilter_init: Initialize the filter module, that is, insert the module into the single-chain table of the HTTP filter module. The insertion method has been described above.
  • Ngx_http_myfilter_create_conf: Allocate space for the structure ngx_http_myfilter_conf_t that stores the configuration item and initialize the struct member.
  • Ngx_http_myfilter_merge_conf: combines configuration items of the same name at the main and SRV levels.

An important structure ngx_command_t is defined as follows:
Static ngx_command_t syntax [] ={{ ngx_string ("myfilter"), ngx_http_main_conf | ngx_http_srv_conf | parser | signature | ngx_conf_flag, delimiter, // the built-in parsing function delimiter, offsetof (delimiter, enable), null}, ngx_null_command };


The default configuration item parsing function ngx_conf_set_flag_slot is used to parse the configuration item myfilter and store the parsed configuration item parameters in the Enable Member of the structure ngx_http_myfilter_conf_t. The configuration item myfilter can only be "on" or "Off ".
Note that the HTTP filter module still belongs to an HTTP module, that is, ngx_module_t.type = ngx_http_module
The following is the core part of the HTTP filter module, that is, the functions pointed to by the two pointers respectively. First, let's look at the functions that process the HTTP Response Header:
// Handle the request header static ngx_int_t forward (ngx_http_request_t * r) {rj* CTX; ngx_http_myfilter_conf_t * conf; // If the returned result is not successful, you do not need to check whether the prefix is added, directly submit it to the next filter module // process the case where the response code is not 200 if (R-> headers_out.status! = Ngx_http_ OK) return ngx_http_next_header_filter (r); // obtain the HTTP context CTX = ngx_http_get_module_ctx (R, ngx_http_myfilter_module); If (CTX) // The request context already exists, this indicates that this module has been called once and is directly handled by the next filter module. Return ngx_http_next_header_filter (r); // obtain the ngx_http_myfilter_conf_t struct conf = struct (R, ngx_http_myfilter_module) of the storage configuration item ); // If the Enable Member is 0, that is, the configuration file does not configure the add_prefix configuration item, // or the parameter value of the add_prefix configuration item is off, in this case, the next filter module directly processes if (conf-> enable = 0) return ngx_http_next_header_filter (r); // constructs the HTTP Context Structure ngx_http_myfilter_ctx_t CTX = ngx_pcalloc (R-> pool, sizeof (ngx_http_myfilter_ctx_t); If (CTX = NULL) return ngx_error; // If add_prefix is 0, no prefix CTX-> add_prefix = 0; // set the constructed context to ngx_http_set_ctx (R, CTX, ngx_http_myfilter_module) in the current request ); // The myfilter filter module only processes HTTP response if (R-> headers_out.content_type.len> = sizeof ("text/plain") whose content-type is "text/plain ") -1 & ngx_strncasecmp (R-> headers_out.content_type.data, (u_char *) "text/plain", sizeof ("text/plain")-1) = 0) {CTX-> add_prefix = 1; // 1 indicates that the prefix must be added before the HTTP packet body. // If the processing module has written the length of the HTTP packet body in Content-Length, because // we have added a prefix string, we need to add the length of this string to the/Content-Length if (R-> headers_out.content_length_n> 0) r-> headers_out.content_length_n + = filter_prefix.len;} // submit it to the next filter module for further processing. Return ngx_http_next_header_filter (r );}


Note the following points. When the module encounters an error response or an error in the module itself, it should not exit the program but be handled by the next HTTP filter module. This is why the above Code calls the ngx_http_next_header_filter function multiple times. In addition, the function must first determine whether to enable the filter function in the configuration file and check the response type. The flowchart is as follows:

Next is the function for processing the http package body:
// This prefix will be added to the package body: static ngx_str_t filter_prefix = ngx_string ("~~~~~~~ This is a prefix ~~~~~~~ \ N "); // The requested package Body Static ngx_int_t forward (ngx_http_request_t * r, ngx_chain_t * In) {forward * CTX; // obtain the HTTP context CTX = ngx_http_get_module_ctx (R, R, ngx_http_myfilter_module); // if the context cannot be obtained or the add_prefix in the context structure is 0 or 2, // no prefix is added, in this case, the next HTTP filter module is used to process if (CTX = NULL | CTX-> add_prefix! = 1) return ngx_http_next_body_filter (R, In); // set add_prefix to 2, so that even if ngx_http_myfilter_body_filter calls back again, the prefix CTX-> add_prefix = 2 is not added; // allocate memory from the requested memory pool to store the string prefix ngx_buf_t * B = ngx_create_temp_buf (R-> pool, filter_prefix.len ); // point the pointer in ngx_buf_t to the filter_prefix string B-> Start = B-> Pos = filter_prefix.data; B-> last = B-> POS + filter_prefix.len; // generate the ngx_chain_t linked list from the requested memory pool and set the newly allocated ngx_buf_t to // Its Buf member, add it to ngx_chain_t * Cl = ngx_alloc_chain_link (R-> pool); CL-> Buf = B; CL-> next = in; // call the HTTP packet body Processing Method of the next module. Note that the newly generated Cl linked list return ngx_http_next_body_filter (R, Cl) is passed in );}


According to this Cl-> next = in, we can judge that we add the strings contained in filter_prefix to the front of the HTTP response package body. The flowchart of the above functions is as follows:

The following is the compilation and demonstration process. We add a prefix to the sent information based on the first mytest module. To compile the two modules into nginx at the same time, run the following command in config:
./configure --add-module="/work/nginx/modules/mytest /work/nginx/modules/myfilter"


Then check whether two modules are successfully included. Here we can view the ngx_modules [] array that saves all modules:
vim objs/ngx_modules.c


The result is as follows:

As you can see, both the HTTP module mytest and the HTTP filter module myfilter are included. Next, make and make install. After the installation is successful, we need to modify the configuration file:
vim /usr/local/nginx/conf/nginx.conf


Shows how to modify the configuration file:

When the URI entered by the client is/Nestle, The mytest and myfilter modules are started. The running results are as follows:

We can see that the HTTP filter module successfully added our preset string before the content of the response package.
The complete code of the HTTP filter module is as follows:
# Include <ngx_config.h> # include <ngx_core.h> # include <ngx_http.h> // configuration item struct typedef struct {ngx_flag_t enable;} struct; // context struct typedef struct {ngx_int_t add_prefix ;} ngx_http_myfilter_ctx_t; ngx_module_t ngx_http_myfilter_module; // declare static policngx_http_next_header_filter; static policngx_http_next_body_filter; // The prefix that needs to be added. Stat IC ngx_str_t filter_prefix = ngx_string ("~~~~~~~ This is a prefix ~~~~~~~ \ N "); static ngx_int_t encode (ngx_http_request_t * r) {ngx_http_myfilter_ctx_t * CTX; ngx_http_myfilter_conf_t * conf; If (R-> headers_out.status! = Ngx_http_ OK) return ngx_http_next_header_filter (r); // process CTX = assign (R, ngx_http_myfilter_module) by the next filter module; If (CTX) return ngx_http_next_header_filter (R ); // The context already exists and will not process conf = ngx_http_get_module_loc_conf (R, ngx_http_myfilter_module); // obtain the config map if (conf-> enable = 0) return ngx_http_next_header_filter (R ); // This filter module does not enable CTX = ngx_pcalloc (R-> pool, sizeof (ngx_http_myfilter_ctx_t); // create a context knot Body if (CTX = NULL) return ngx_error; CTX-> add_prefix = 0; // 0 indicates no need to add the prefix ngx_http_set_ctx (R, CTX, ngx_http_myfilter_module ); // only process HTTP requests whose content-type is text or plain If (R-> headers_out.content_type.len> = sizeof ("text/plain ") -1 & ngx_strncasecmp (R-> headers_out.content_type.data, (u_char *) "text/plain", sizeof ("text/plain")-1) = 0) {CTX-> add_prefix = 1; // 1 indicates that you need to add the prefix if (R-> headers_out.content_length_n> 0) r-> Response + = filter_prefix.len; // response package length is increased} return ngx_http_next_header_filter (r);} static ngx_int_t forward (ngx_http_request_t * r, ngx_chain_t * In) {rj* CTX; CTX = ngx_http_get_module_ctx (R, ngx_http_myfilter_module); // obtain the context structure if (CTX = NULL | CTX-> add_prefix! = 1) return ngx_http_next_body_filter (R, In); // do not add the prefix CTX-> add_prefix = 2; // 2 indicates that the prefix ngx_buf_t * B = ngx_create_temp_buf (R-> pool, filter_prefix.len) has been added; B-> Start = B-> Pos = filter_prefix.data; b-> last = B-> POS + filter_prefix.len; // link the ngx_chain_t * Cl = ngx_alloc_chain_link (R-> pool); CL-> Buf = B; CL-> next = In; return ngx_http_next_body_filter (R, Cl); // jump to the next filter module} // initialize the HTTP filter module static ngx_int_t ngx_http_myfilter_init (ngx_conf_t * Cf) {ngx_http_next_header_filter = response; ngx_http_top_header_filter = response; response = response; return ngx_ OK;} // create a structure static void * response (ngx_conf_t * Cf) for storing configuration items) {optional * mycf; mycf = (ngx_http_myfilter_conf_t *) ngx_pcalloc (CF-> pool, sizeof (ngx_http_myfilter_conf_t); If (mycf = NULL) return NULL; mycf-> enable = ngx_conf_unset; return mycf;} // merge the configuration item static char * ngx_http_myfilter_merge_conf (ngx_conf_t * Cf, void * parent, void * child) {optional * Prev = (ngx_http_myfilter_conf_t *) parent; ngx_http_myfilter_conf_t * conf = (optional *) child; ngx_conf_merge_value (conf-> enable, Prev-> enable, 0 ); // Merge function return ngx_conf_ OK;} static ngx_command_t character [] ={{ ngx_string ("myfilter"), ngx_http_main_conf | ngx_http_srv_conf | Operator | ngx_conf_flag, expires, expires, offsetof (outputs, enable), null,}, ngx_null_command}; // eight functions called during HTTP framework initialization static ngx_http_module_t functions = {null, ngx_http_myfilter_init, null, null, counts, counts,}; // defines an HTTP module ngx_module_t ngx_http_myfilter_module = {ngx_module_v1, //, 0, 0, 0, 0, 1 & amp; s, counts, ngx_http_module, null, null, null, null, ngx_module_v1_padding, // 0, 0, 0, 0, 0, 0, reserved field };



For details, see chapter 6 of nginx.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.