Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Nginx more handler module sample analysis


May 23, 2021 Nginx Getting started


Table of contents


More handler module sample analysis

http access module

The code for this module is src/http/modules/ngx_http_access_module.c system. T he role of this module is to provide access control to clients of a particular host. You can restrict access to all clients of a particular host to the service side, or to a server, or to a location.

The implementation of this module is very simple, there are only a few functions in total.

    static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
    static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,
        ngx_http_access_loc_conf_t *alcf, in_addr_t addr);
    #if (NGX_HAVE_INET6)
    static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,
        ngx_http_access_loc_conf_t *alcf, u_char *p);
    #endif
    static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);
    static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
        void *conf);
    static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);
    static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,
        void *parent, void *child);
    static ngx_int_t ngx_http_access_init(ngx_conf_t *cf);

For several functions related to the configuration do not need to be explained, it is important to mention the function ngx_http_access_init, which in the implementation of the module mounted to the NGX_HTTP_ACCESS_PHASE stage handler, so that their call time occurred before the NGX_HTTP_CONTENT_PHASE and other stages. Because of the client address restriction check, there is no need to wait until after this.

Also take a look at the main handler of this module ngx_http_access_handler. The logic of this function is also very simple, mainly depending on the type of client address, to choose the ipv4 type of handler ngx_http_access_inet or ipv6 type of handler ngx_http_access_inet6.

The inside of these two handler is also very simple, that is, the loop checks each rule to see if there are matching rules, if there is a match returned results, if there is no match, the default deny.

http static module

To some extent, this module can be counted as the "most authentic" and "oldest" content handler. B ecause the purpose of this module is to read static files on disk and use the contents of the files as the resulting output. I n the early days of Web technology, there were only static pages and no server-side scripts to dynamically generate HTML. I'm afraid when developing a Web server, the first one to develop is such a content handler.

The code for the http static module is src/http/modules/ngx_http_static_module.c with a total of just over two hundred lines and nearly three hundred lines. Can be said to be very short.

Let's first look at the definition of the module context of the module.

    ngx_http_module_t  ngx_http_static_module_ctx = {
        NULL,                                  /* preconfiguration */
        ngx_http_static_init,                  /* postconfiguration */

        NULL,                                  /* create main configuration */
        NULL,                                  /* init main configuration */

        NULL,                                  /* create server configuration */
        NULL,                                  /* merge server configuration */

        NULL,                                  /* create location configuration */
        NULL                                   /* merge location configuration */
    };

It's very concise, not even any configuration-related functions. T hat's right, because the module doesn't provide any configuration instructions. A s you can see, this module is too simple to do, and there is really nothing to configure. T he only function that needs to be called is ngx_http_static_init function. Well, let's take a look at what this function does.

    static ngx_int_t
    ngx_http_static_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;
        }

        *h = ngx_http_static_handler;

        return NGX_OK;
    }

Just mount this handler to NGX_HTTP_CONTENT_PHASE processing phase. Simple, isn't it?

Let's take a look at the functionality of the system where the core processing logic of this module ngx_http_static_handler is located. This function accounts for about eighty-nine percent of the module's code.

    static ngx_int_t
    ngx_http_static_handler(ngx_http_request_t *r)
    {
        u_char                    *last, *location;
        size_t                     root, len;
        ngx_str_t                  path;
        ngx_int_t                  rc;
        ngx_uint_t                 level;
        ngx_log_t                 *log;
        ngx_buf_t                 *b;
        ngx_chain_t                out;
        ngx_open_file_info_t       of;
        ngx_http_core_loc_conf_t  *clcf;

        if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
            return NGX_HTTP_NOT_ALLOWED;
        }

        if (r->uri.data[r->uri.len - 1] == '/') {
            return NGX_DECLINED;
        }

        log = r->connection->log;

        /*
         * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
         * so we do not need to reserve memory for '/' for possible redirect
         */

        last = ngx_http_map_uri_to_path(r, &path, &root, 0);
        if (last == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        path.len = last - path.data;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                       "http filename: \"%s\"", path.data);

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        ngx_memzero(&of, sizeof(ngx_open_file_info_t));

        of.read_ahead = clcf->read_ahead;
        of.directio = clcf->directio;
        of.valid = clcf->open_file_cache_valid;
        of.min_uses = clcf->open_file_cache_min_uses;
        of.errors = clcf->open_file_cache_errors;
        of.events = clcf->open_file_cache_events;

        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
            != NGX_OK)
        {
            switch (of.err) {

            case 0:
                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            case NGX_ENOENT:
            case NGX_ENOTDIR:
            case NGX_ENAMETOOLONG:

                level = NGX_LOG_ERR;
                rc = NGX_HTTP_NOT_FOUND;
                break;

            case NGX_EACCES:
    #if (NGX_HAVE_OPENAT)
            case NGX_EMLINK:
            case NGX_ELOOP:
    #endif

                level = NGX_LOG_ERR;
                rc = NGX_HTTP_FORBIDDEN;
                break;

            default:

                level = NGX_LOG_CRIT;
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                break;
            }

            if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
                ngx_log_error(level, log, of.err,
                              "%s \"%s\" failed", of.failed, path.data);
            }

            return rc;
        }

        r->root_tested = !r->error_page;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);

        if (of.is_dir) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");

            ngx_http_clear_location(r);

            r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
            if (r->headers_out.location == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            len = r->uri.len + 1;

            if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
                location = path.data + clcf->root.len;

                *last = '/';

            } else {
                if (r->args.len) {
                    len += r->args.len + 1;
                }

                location = ngx_pnalloc(r->pool, len);
                if (location == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }

                last = ngx_copy(location, r->uri.data, r->uri.len);

                *last = '/';

                if (r->args.len) {
                    *++last = '?';
                    ngx_memcpy(++last, r->args.data, r->args.len);
                }
            }

            /*
             * we do not need to set the r->headers_out.location->hash and
             * r->headers_out.location->key fields
             */

            r->headers_out.location->value.len = len;
            r->headers_out.location->value.data = location;

            return NGX_HTTP_MOVED_PERMANENTLY;
        }

    #if !(NGX_WIN32) /* the not regular files are probably Unix specific */

        if (!of.is_file) {
            ngx_log_error(NGX_LOG_CRIT, log, 0,
                          "\"%s\" is not a regular file", path.data);

            return NGX_HTTP_NOT_FOUND;
        }

    #endif

        if (r->method & NGX_HTTP_POST) {
            return NGX_HTTP_NOT_ALLOWED;
        }

        rc = ngx_http_discard_request_body(r);

        if (rc != NGX_OK) {
            return rc;
        }

        log->action = "sending response to client";

        r->headers_out.status = NGX_HTTP_OK;
        r->headers_out.content_length_n = of.size;
        r->headers_out.last_modified_time = of.mtime;

        if (ngx_http_set_content_type(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (r != r->main && of.size == 0) {
            return ngx_http_send_header(r);
        }

        r->allow_ranges = 1;

        /* we need to allocate all before the header would be sent */

        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
        if (b->file == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        rc = ngx_http_send_header(r);

        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
            return rc;
        }

        b->file_pos = 0;
        b->file_last = of.size;

        b->in_file = b->file_last ? 1: 0;
        b->last_buf = (r == r->main) ? 1: 0;
        b->last_in_chain = 1;

        b->file->fd = of.fd;
        b->file->name = path;
        b->file->log = log;
        b->file->directio = of.is_directio;

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

        return ngx_http_output_filter(r, &out);
    }

The first step is to check the client's http request type (r-gt;method) if the request NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST the processing continues, otherwise all returns to NGX_HTTP_NOT_ALLOWED reject the client's initiated request.

The second is to check whether the end character of the requested url is slash / if it is a statement that the request is not a file, give subsequent handler to process, such as the subsequent ngx_http_autoindex_handler (if the request is under a directory, you can list the files for that directory), or ngx_http_index_handler (if there is a default index file below the path of the request, return directly to the contents of the index file).

Then a ngx_http_map_uri_to_path function is called to convert the path of the requested http protocol into the path of a file system.

Then according to the specific path of conversion, to open the file, open the file when doing 2 kinds of checks, one is, if the requested file is a symbol link, according to the configuration, whether to allow symbolic links, not to return errors. A nother check is that if the request is a name, a directory name, also returns an error. I f there are no errors, read the file and return the contents. In fact, it may not be particularly accurate to return the content, the more accurate statement is that the resulting content passed to the subsequent filter to deal with.

http log module

The module provides the ability to log every http request, which is what we see .log. Of course, this module provides some configuration instructions for log, making it easier to customize access .log.

The code for this module is src/http/modules/ngx_http_log_module.c although the module has nearly 1400 lines of code, the main logic lies in the processing of details such as the format of the log itself. Our analysis here is primarily concerned with how to write a log handler question.

Since the parameters we get when we log handler are also requesting, that means we can study the structure and record all the information we need if we need it.

For log handler, it is particularly important to note that log handler is called anyway, that is, as long as the service side accepts a request from a client, that is, a request object is generated, then the handle of these loger will be called, that is, when the request is released (ngx_http_free_request function).

Then of course it must not be forgotten that log handler is the best, but also recommended to be mounted in NGX_HTTP_LOG_PHASE stage. Because mounting at other stages can be skipped in some cases without execution, your log module records incomplete information.

It is also important to note that since Nginx is allowed to have more than one handler module at one stage, it is determined whether to call the next handler based on its processing results. B ut for handler mounted NGX_HTTP_LOG_PHASE stage, the return value of the handler's specific handler here is not of concern at all, and all are called. As follows, src/http/ngx_http_request.c

    static void
    ngx_http_log_request(ngx_http_request_t *r)
    {
        ngx_uint_t                  i, n;
        ngx_http_handler_pt        *log_handler;
        ngx_http_core_main_conf_t  *cmcf;

        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

        log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
        n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;

        for (i = 0; i < n; i++) {
            log_handler[i](r);
        }
    }