nginx http module configuration merge

Keywords: Programming Nginx Attribute

When configuring the nginx.conf file, it is easy to see that some of the configuration items can be configured in either an http block, a server block, or a location block.But not all configuration items can be configured anywhere. Depending on the role they play, nginx defines where each configuration block can be used.Since a configuration item can be configured in more than one configuration block, the question here is which configuration item prevails when processing requests.This article mainly explains how nginx combines configuration items.

In the previous article, we explained the basic storage structure of the nginx http module. Readers are strongly encouraged to read this article before reading it.The following is a schematic diagram of the storage structure of the nginx http module:

Here we will not dwell on how nginx parses configuration items to form such a storage structure.Nginx merges configuration items mainly through the ngx_http_merge_servers() method, which has the following source code:

/**
 * @param cf ngx_conf_t object running throughout nginx
 * @param cmcf Configuration objects corresponding to core modules
 * @param module Currently traversed module during outer traversal
 * @param ctx_index When traversing the outer layer, the configuration of the currently traversed module corresponds to the storage location
 */
static char * ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
                       ngx_http_module_t *module, ngx_uint_t ctx_index) {
  char *rv;
  ngx_uint_t s;
  ngx_http_conf_ctx_t *ctx, saved;
  ngx_http_core_loc_conf_t *clcf;
  ngx_http_core_srv_conf_t **cscfp;

  // Here you get the ngx_http_core_srv_conf_t structure corresponding to all server configuration blocks, each corresponding to a configuration file
  // Configuration of a server block
  cscfp = cmcf->servers.elts;
  // Here cf->ctx is the ngx_http_conf_ctx_t structure obtained by parsing the HTTP configuration block, and it should be noted that only main_conf is present
  // The corresponding array data makes sense because the http block's data is stored only in main_conf
  ctx = (ngx_http_conf_ctx_t *) cf->ctx;
  // Note here that after executing saved=*ctx, the structure object of the current *ctx will be completely copied and a new one will be stored in
  // In saved, that is, the values of ctx and & saved are different.Executed in the for loop below
  // Ctx->srv_conf = cscfp[s]->ctx->srv_conf; statement, this actually changes
  // Instead of affecting the saved->srv_conf attribute value, the ctx pointer points to the value of the srv_conf attribute of the structure object.
  // Because saved and *ctx are two different structured objects, each with the same attribute value.
  saved = *ctx;
  rv = NGX_CONF_OK;

  for (s = 0; s < cmcf->servers.nelts; s++) {

    /* merge the server{}s' srv_conf's */

    ctx->srv_conf = cscfp[s]->ctx->srv_conf;

    if (module->merge_srv_conf) {
      // This is mainly a merge of configurations, saved.srv_conf[ctx_index] points to the configuration of the SRV type parsed out in the http block.
      // Cscfp[s]->ctx->srv_conf[ctx_index] resolves the configuration in the current server block, and the general rule for merging is through the corresponding
      // The merge_srv_conf() implemented by the module will do this, using either the configuration in the http block or the configuration in the server block.
      // Or merging the two is determined by concrete implementation.The effect of merging here is essentially to
      // Attribute assignment in cscfp[s]->ctx->srv_conf[ctx_index], if the value does not exist, write the value corresponding to the http block to
      // In this property
      rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], 
                                  cscfp[s]->ctx->srv_conf[ctx_index]);
      if (rv != NGX_CONF_OK) {
        goto failed;
      }
    }

    if (module->merge_loc_conf) {
      ctx->loc_conf = cscfp[s]->ctx->loc_conf;

      // It is important to note that the main merge here is the configuration data of the corresponding LOC type in the http block and the current server block, which did not begin at this time
      // Merge the location configurations under the server block
      rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], 
                                  cscfp[s]->ctx->loc_conf[ctx_index]);
      if (rv != NGX_CONF_OK) {
        goto failed;
      }

      clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

      // Here clcf->location S stores the configuration of all location blocks under the current server block, while cscfp[s]->ctx->loc_conf
      // Then it points to the configuration data after merging the http block and the server block, from which you can see that the result of the current method is merging
      // Configuration of http, server, and location blocks
      rv = ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, 
                                    module, ctx_index);
      if (rv != NGX_CONF_OK) {
        goto failed;
      }
    }
  }

  failed:

  *ctx = saved;

  return rv;
}

The ngx_http_merge_servers() method here accomplishes the following main tasks:

  • Calling the merge_srv_conf() method of the current module merges the configuration items of NGX_HTTP_SRV_CONF_OFFSET type in the http configuration block and the server configuration block, also merges the srv_conf array in the http configuration block structure with the srv_conf array in the server configuration block structure, thus completing the merge of the NGX_HTTP_SRV_CONF_OFFSET type configuration items;
  • Calling the merge_loc_conf() method of the current module merges the configuration items of NGX_HTTP_LOC_CONF_OFFSET type in the http configuration block and the server configuration block, which also merges the loc_conf array in the http configuration block structure with the loc_conf array in the server configuration block structure.However, unlike configuration items of type NGX_HTTP_SRV_CONF_OFFSET, configuration items of type NGX_HTTP_LOC_CONF_OFFSET can also be configured in the location configuration block, so the results of the merge need to be merged with the configuration items in the location configuration block.
  • The ngx_http_merge_locations() method is called to continue merging configuration items of type NGX_HTTP_LOC_CONF_OFFSET merged from the previous HTTP configuration block with the server configuration block to obtain the final merge result.One thing to note here, however, is that the location configuration block can continue to configure the configuration Sublocation configuration block so that it keeps going, whereas the ngx_http_merge_locations() method here can be used for recursive calls to merge the sublocations.

Here we continue to see how the ngx_http_merge_locations() method merges child locations:

/**
 * Configuration for merging location blocks
 *
 * @param cf Configuration structure ngx_http_conf_ctx_t pointing to the current HTTP block
 * @param locations Configuration of all location blocks under the current server block
 * @param loc_conf
 * @param module
 * @param ctx_index
 * @return
 */
static char * ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, void **loc_conf,
    ngx_http_module_t *module, ngx_uint_t ctx_index) {
  char *rv;
  ngx_queue_t *q;
  ngx_http_conf_ctx_t *ctx, saved;
  ngx_http_core_loc_conf_t *clcf;
  ngx_http_location_queue_t *lq;

  if (locations == NULL) {
    return NGX_CONF_OK;
  }

  ctx = (ngx_http_conf_ctx_t *) cf->ctx;
  saved = *ctx;

  for (q = ngx_queue_head(locations);
       q != ngx_queue_sentinel(locations);
       q = ngx_queue_next(q)) {
    lq = (ngx_http_location_queue_t *) q;

    clcf = lq->exact ? lq->exact : lq->inclusive;
    ctx->loc_conf = clcf->loc_conf;

    // Merge the configuration of the location block, where loc_conf[ctx_index] stores the data merged by the http block and the server block configuration.
    // Clcf->loc_conf is the data of a location block under the current server block, in which case the current loc_conf
    // Merge data into clcf->loc_conf
    rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]);
    if (rv != NGX_CONF_OK) {
      return rv;
    }

    // Since Sublocation blocks can also be configured in the location block, merging occurs recursively here
    rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index);
    if (rv != NGX_CONF_OK) {
      return rv;
    }
  }

  *ctx = saved;

  return NGX_CONF_OK;
}

The merge process for location and child location is actually simple, that is, to pop up each child location under the current configuration block (server or parent location) in turn, and then call the merge_loc_conf() method of the current module to merge the configuration of the current configuration block with the pop-up child location.After the merge is complete, continue the recursive merge of the child location, that is, merge the child location with its child location.This continues until all locations are merged.

Configuration item merging for nginx is an important feature, such as a configuration item that can be configured in either an http block or a server block. If only the configuration in an http block is configured in nginx.conf, then the configuration of all server blocks can inherit the configuration of an http block, but if one of the servers itself has also configured the configuration itemThis allows the configuration item to use its own configuration directly when merging without inheriting the configuration of the http configuration block, which allows for very flexible functionality.

Posted by meburke on Mon, 24 Feb 2020 17:51:36 -0800