src/ - nginx-1.7.9

core/

event/

http/

mail/

misc/

os/

src/core/ - nginx-1.7.9

nginx.c

nginx.h

ngx_array.c

ngx_array.h

ngx_buf.c

ngx_buf.h

ngx_conf_file.c

ngx_conf_file.h

ngx_config.h

ngx_connection.c

ngx_connection.h

ngx_core.h

ngx_cpuinfo.c

ngx_crc.h

ngx_crc32.c

ngx_crc32.h

ngx_crypt.c

ngx_crypt.h

ngx_cycle.c

ngx_cycle.h

ngx_file.c

ngx_file.h

ngx_hash.c

ngx_hash.h

ngx_inet.c

ngx_inet.h

ngx_list.c

ngx_list.h

ngx_log.c

ngx_log.h

ngx_md5.c

ngx_md5.h

ngx_murmurhash.c

ngx_murmurhash.h

ngx_open_file_cache.c

ngx_open_file_cache.h

ngx_output_chain.c

ngx_palloc.c

ngx_palloc.h

ngx_parse.c

ngx_parse.h

ngx_proxy_protocol.c

ngx_proxy_protocol.h

ngx_queue.c

ngx_queue.h

ngx_radix_tree.c

ngx_radix_tree.h

ngx_rbtree.c

ngx_rbtree.h

ngx_regex.c

ngx_regex.h

ngx_resolver.c

ngx_resolver.h

ngx_sha1.h

ngx_shmtx.c

ngx_shmtx.h

ngx_slab.c

ngx_slab.h

ngx_spinlock.c

ngx_string.c

ngx_string.h

ngx_syslog.c

ngx_syslog.h

ngx_times.c

ngx_times.h

src/core/nginx.c - nginx-1.7.9

Global variables defined

ngx_conf_file

ngx_conf_params

ngx_core_commands

ngx_core_module

ngx_core_module_ctx

ngx_debug_points

ngx_max_module

ngx_os_environ

ngx_prefix

ngx_show_configure

ngx_show_help

ngx_show_version

ngx_signal

Functions defined

main

ngx_add_inherited_sockets

ngx_core_module_create_conf

ngx_core_module_init_conf

ngx_exec_new_binary

ngx_get_cpu_affinity

ngx_get_options

ngx_process_options

ngx_save_argv

ngx_set_cpu_affinity

ngx_set_env

ngx_set_environment

ngx_set_priority

ngx_set_user

ngx_set_worker_processes

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <nginx.h>

static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);

static ngx_int_t ngx_get_options(int argc, char *const *argv);

static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);

static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);

static void *ngx_core_module_create_conf(ngx_cycle_t *cycle);

static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf);

static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_conf_enum_t  ngx_debug_points[] = {

    { ngx_string("stop"), NGX_DEBUG_POINTS_STOP },

    { ngx_string("abort"), NGX_DEBUG_POINTS_ABORT },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_core_commands[] = {

    { ngx_string("daemon"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      0,

      offsetof(ngx_core_conf_t, daemon),

      NULL },

    { ngx_string("master_process"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      0,

      offsetof(ngx_core_conf_t, master),

      NULL },

    { ngx_string("timer_resolution"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      0,

      offsetof(ngx_core_conf_t, timer_resolution),

      NULL },

    { ngx_string("pid"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      0,

      offsetof(ngx_core_conf_t, pid),

      NULL },

    { ngx_string("lock_file"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      0,

      offsetof(ngx_core_conf_t, lock_file),

      NULL },

    { ngx_string("worker_processes"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_set_worker_processes,

      0,

      0,

      NULL },

    { ngx_string("debug_points"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      0,

      offsetof(ngx_core_conf_t, debug_points),

      &ngx_debug_points },

    { ngx_string("user"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12,

      ngx_set_user,

      0,

      0,

      NULL },

    { ngx_string("worker_priority"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_set_priority,

      0,

      0,

      NULL },

    { ngx_string("worker_cpu_affinity"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,

      ngx_set_cpu_affinity,

      0,

      0,

      NULL },

    { ngx_string("worker_rlimit_nofile"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_core_conf_t, rlimit_nofile),

      NULL },

    { ngx_string("worker_rlimit_core"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_off_slot,

      0,

      offsetof(ngx_core_conf_t, rlimit_core),

      NULL },

    { ngx_string("worker_rlimit_sigpending"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_core_conf_t, rlimit_sigpending),

      NULL },

    { ngx_string("working_directory"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      0,

      offsetof(ngx_core_conf_t, working_directory),

      NULL },

    { ngx_string("env"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_set_env,

      0,

      0,

      NULL },

#if (NGX_THREADS)

    { ngx_string("worker_threads"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_core_conf_t, worker_threads),

      NULL },

    { ngx_string("thread_stack_size"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      0,

      offsetof(ngx_core_conf_t, thread_stack_size),

      NULL },

#endif

      ngx_null_command

};

‌static ngx_core_module_t  ngx_core_module_ctx = {

    ngx_string("core"),

    ngx_core_module_create_conf,

    ngx_core_module_init_conf

};

‌ngx_module_t  ngx_core_module = {

    NGX_MODULE_V1,

    &ngx_core_module_ctx,                  /* module context */

    ngx_core_commands,                     /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌ngx_uint_t          ngx_max_module;

‌static ngx_uint_t   ngx_show_help;

‌static ngx_uint_t   ngx_show_version;

‌static ngx_uint_t   ngx_show_configure;

‌static u_char      *ngx_prefix;

‌static u_char      *ngx_conf_file;

‌static u_char      *ngx_conf_params;

‌static char        *ngx_signal;

‌static char **ngx_os_environ;

int ngx_cdecl

‌main(int argc, char *const *argv)

{

    ngx_int_t         i;

    ngx_log_t        *log;

    ngx_cycle_t      *cycle, init_cycle;

    ngx_core_conf_t  *ccf;

    ngx_debug_init();

    if (ngx_strerror_init() != NGX_OK) {

        return 1;

    }

    if (ngx_get_options(argc, argv) != NGX_OK) {

        return 1;

    }

    if (ngx_show_version) {

        ngx_write_stderr("nginx version: " NGINX_VER_BUILD NGX_LINEFEED);

        if (ngx_show_help) {

            ngx_write_stderr(

                "Usage: nginx [-?hvVtq] [-s signal] [-c filename] "

                             "[-p prefix] [-g directives]" NGX_LINEFEED

                             NGX_LINEFEED

                "Options:" NGX_LINEFEED

                "  -?,-h         : this help" NGX_LINEFEED

                "  -v            : show version and exit" NGX_LINEFEED

                "  -V            : show version and configure options then exit"

                                   NGX_LINEFEED

                "  -t            : test configuration and exit" NGX_LINEFEED

                "  -q            : suppress non-error messages "

                                   "during configuration testing" NGX_LINEFEED

                "  -s signal     : send signal to a master process: "

                                   "stop, quit, reopen, reload" NGX_LINEFEED

#ifdef NGX_PREFIX

                "  -p prefix     : set prefix path (default: "

                                   NGX_PREFIX ")" NGX_LINEFEED

#else

                "  -p prefix     : set prefix path (default: NONE)" NGX_LINEFEED

#endif

                "  -c filename   : set configuration file (default: "

                                   NGX_CONF_PATH ")" NGX_LINEFEED

                "  -g directives : set global directives out of configuration "

                                   "file" NGX_LINEFEED NGX_LINEFEED

                );

        }

        if (ngx_show_configure) {

            ngx_write_stderr(

#ifdef NGX_COMPILER

                "built by " NGX_COMPILER NGX_LINEFEED

#endif

#if (NGX_SSL)

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

                "TLS SNI support enabled" NGX_LINEFEED

#else

                "TLS SNI support disabled" NGX_LINEFEED

#endif

#endif

                "configure arguments:" NGX_CONFIGURE NGX_LINEFEED);

        }

        if (!ngx_test_config) {

            return 0;

        }

    }

    /* TODO */ ngx_max_sockets = -1;

    ngx_time_init();

#if (NGX_PCRE)

    ngx_regex_init();

#endif

    ngx_pid = ngx_getpid();

    log = ngx_log_init(ngx_prefix);

    if (log == NULL) {

        return 1;

    }

    /* STUB */

#if (NGX_OPENSSL)

    ngx_ssl_init(log);

#endif

    /*

     * init_cycle->log is required for signal handlers and

     * ngx_process_options()

     */

    ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));

    init_cycle.log = log;

    ngx_cycle = &init_cycle;

    init_cycle.pool = ngx_create_pool(1024, log);

    if (init_cycle.pool == NULL) {

        return 1;

    }

    if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {

        return 1;

    }

    if (ngx_process_options(&init_cycle) != NGX_OK) {

        return 1;

    }

    if (ngx_os_init(log) != NGX_OK) {

        return 1;

    }

    /*

     * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()

     */

    if (ngx_crc32_table_init() != NGX_OK) {

        return 1;

    }

    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {

        return 1;

    }

    ngx_max_module = 0;

    for (i = 0; ngx_modules[i]; i++) {

        ngx_modules[i]->index = ngx_max_module++;

    }

    cycle = ngx_init_cycle(&init_cycle);

    if (cycle == NULL) {

        if (ngx_test_config) {

            ngx_log_stderr(0, "configuration file %s test failed",

                           init_cycle.conf_file.data);

        }

        return 1;

    }

    if (ngx_test_config) {

        if (!ngx_quiet_mode) {

            ngx_log_stderr(0, "configuration file %s test is successful",

                           cycle->conf_file.data);

        }

        return 0;

    }

    if (ngx_signal) {

        return ngx_signal_process(cycle, ngx_signal);

    }

    ngx_os_status(cycle->log);

    ngx_cycle = cycle;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {

        ngx_process = NGX_PROCESS_MASTER;

    }

#if !(NGX_WIN32)

    if (ngx_init_signals(cycle->log) != NGX_OK) {

        return 1;

    }

    if (!ngx_inherited && ccf->daemon) {

        if (ngx_daemon(cycle->log) != NGX_OK) {

            return 1;

        }

        ngx_daemonized = 1;

    }

    if (ngx_inherited) {

        ngx_daemonized = 1;

    }

#endif

    if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {

        return 1;

    }

    if (ngx_log_redirect_stderr(cycle) != NGX_OK) {

        return 1;

    }

    if (log->file->fd != ngx_stderr) {

        if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          ngx_close_file_n " built-in log failed");

        }

    }

    ngx_use_stderr = 0;

    if (ngx_process == NGX_PROCESS_SINGLE) {

        ngx_single_process_cycle(cycle);

    } else {

        ngx_master_process_cycle(cycle);

    }

    return 0;

}

static ngx_int_t

‌ngx_add_inherited_sockets(ngx_cycle_t *cycle)

{

    u_char           *p, *v, *inherited;

    ngx_int_t         s;

    ngx_listening_t  *ls;

    inherited = (u_char *) getenv(NGINX_VAR);

    if (inherited == NULL) {

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,

                  "using inherited sockets from \"%s\"", inherited);

    if (ngx_array_init(&cycle->listening, cycle->pool, 10,

                       sizeof(ngx_listening_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    for (p = inherited, v = p; *p; p++) {

        if (*p == ':' || *p == ';') {

            s = ngx_atoi(v, p - v);

            if (s == NGX_ERROR) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                              "invalid socket number \"%s\" in " NGINX_VAR

                              " environment variable, ignoring the rest"

                              " of the variable", v);

                break;

            }

            v = p + 1;

            ls = ngx_array_push(&cycle->listening);

            if (ls == NULL) {

                return NGX_ERROR;

            }

            ngx_memzero(ls, sizeof(ngx_listening_t));

            ls->fd = (ngx_socket_t) s;

        }

    }

    ngx_inherited = 1;

    return ngx_set_inherited_sockets(cycle);

}

char **

‌ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)

{

    char             **p, **env;

    ngx_str_t         *var;

    ngx_uint_t         i, n;

    ngx_core_conf_t   *ccf;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (last == NULL && ccf->environment) {

        return ccf->environment;

    }

    var = ccf->env.elts;

    for (i = 0; i < ccf->env.nelts; i++) {

        if (ngx_strcmp(var[i].data, "TZ") == 0

            || ngx_strncmp(var[i].data, "TZ=", 3) == 0)

        {

            goto tz_found;

        }

    }

    var = ngx_array_push(&ccf->env);

    if (var == NULL) {

        return NULL;

    }

    var->len = 2;

    var->data = (u_char *) "TZ";

    var = ccf->env.elts;

tz_found:

    n = 0;

    for (i = 0; i < ccf->env.nelts; i++) {

        if (var[i].data[var[i].len] == '=') {

            n++;

            continue;

        }

        for (p = ngx_os_environ; *p; p++) {

            if (ngx_strncmp(*p, var[i].data, var[i].len) == 0

                && (*p)[var[i].len] == '=')

            {

                n++;

                break;

            }

        }

    }

    if (last) {

        env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log);

        *last = n;

    } else {

        env = ngx_palloc(cycle->pool, (n + 1) * sizeof(char *));

    }

    if (env == NULL) {

        return NULL;

    }

    n = 0;

    for (i = 0; i < ccf->env.nelts; i++) {

        if (var[i].data[var[i].len] == '=') {

            env[n++] = (char *) var[i].data;

            continue;

        }

        for (p = ngx_os_environ; *p; p++) {

            if (ngx_strncmp(*p, var[i].data, var[i].len) == 0

                && (*p)[var[i].len] == '=')

            {

                env[n++] = *p;

                break;

            }

        }

    }

    env[n] = NULL;

    if (last == NULL) {

        ccf->environment = env;

        environ = env;

    }

    return env;

}

ngx_pid_t

‌ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)

{

    char             **env, *var;

    u_char            *p;

    ngx_uint_t         i, n;

    ngx_pid_t          pid;

    ngx_exec_ctx_t     ctx;

    ngx_core_conf_t   *ccf;

    ngx_listening_t   *ls;

    ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));

    ctx.path = argv[0];

    ctx.name = "new binary process";

    ctx.argv = argv;

    n = 2;

    env = ngx_set_environment(cycle, &n);

    if (env == NULL) {

        return NGX_INVALID_PID;

    }

    var = ngx_alloc(sizeof(NGINX_VAR)

                    + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,

                    cycle->log);

    if (var == NULL) {

        ngx_free(env);

        return NGX_INVALID_PID;

    }

    p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        p = ngx_sprintf(p, "%ud;", ls[i].fd);

    }

    *p = '\0';

    env[n++] = var;

#if (NGX_SETPROCTITLE_USES_ENV)

    /* allocate the spare 300 bytes for the new binary process title */

    env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

#endif

    env[n] = NULL;

#if (NGX_DEBUG)

    {

    char  **e;

    for (e = env; *e; e++) {

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e);

    }

    }

#endif

    ctx.envp = (char *const *) env;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      ngx_rename_file_n " %s to %s failed "

                      "before executing new binary process \"%s\"",

                      ccf->pid.data, ccf->oldpid.data, argv[0]);

        ngx_free(env);

        ngx_free(var);

        return NGX_INVALID_PID;

    }

    pid = ngx_execute(cycle, &ctx);

    if (pid == NGX_INVALID_PID) {

        if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)

            == NGX_FILE_ERROR)

        {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          ngx_rename_file_n " %s back to %s failed after "

                          "an attempt to execute new binary process \"%s\"",

                          ccf->oldpid.data, ccf->pid.data, argv[0]);

        }

    }

    ngx_free(env);

    ngx_free(var);

    return pid;

}

static ngx_int_t

‌ngx_get_options(int argc, char *const *argv)

{

    u_char     *p;

    ngx_int_t   i;

    for (i = 1; i < argc; i++) {

        p = (u_char *) argv[i];

        if (*p++ != '-') {

            ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);

            return NGX_ERROR;

        }

        while (*p) {

            switch (*p++) {

            case '?':

            case 'h':

                ngx_show_version = 1;

                ngx_show_help = 1;

                break;

            case 'v':

                ngx_show_version = 1;

                break;

            case 'V':

                ngx_show_version = 1;

                ngx_show_configure = 1;

                break;

            case 't':

                ngx_test_config = 1;

                break;

            case 'q':

                ngx_quiet_mode = 1;

                break;

            case 'p':

                if (*p) {

                    ngx_prefix = p;

                    goto next;

                }

                if (argv[++i]) {

                    ngx_prefix = (u_char *) argv[i];

                    goto next;

                }

                ngx_log_stderr(0, "option \"-p\" requires directory name");

                return NGX_ERROR;

            case 'c':

                if (*p) {

                    ngx_conf_file = p;

                    goto next;

                }

                if (argv[++i]) {

                    ngx_conf_file = (u_char *) argv[i];

                    goto next;

                }

                ngx_log_stderr(0, "option \"-c\" requires file name");

                return NGX_ERROR;

            case 'g':

                if (*p) {

                    ngx_conf_params = p;

                    goto next;

                }

                if (argv[++i]) {

                    ngx_conf_params = (u_char *) argv[i];

                    goto next;

                }

                ngx_log_stderr(0, "option \"-g\" requires parameter");

                return NGX_ERROR;

            case 's':

                if (*p) {

                    ngx_signal = (char *) p;

                } else if (argv[++i]) {

                    ngx_signal = argv[i];

                } else {

                    ngx_log_stderr(0, "option \"-s\" requires parameter");

                    return NGX_ERROR;

                }

                if (ngx_strcmp(ngx_signal, "stop") == 0

                    || ngx_strcmp(ngx_signal, "quit") == 0

                    || ngx_strcmp(ngx_signal, "reopen") == 0

                    || ngx_strcmp(ngx_signal, "reload") == 0)

                {

                    ngx_process = NGX_PROCESS_SIGNALLER;

                    goto next;

                }

                ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);

                return NGX_ERROR;

            default:

                ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));

                return NGX_ERROR;

            }

        }

    next:

        continue;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv)

{

#if (NGX_FREEBSD)

    ngx_os_argv = (char **) argv;

    ngx_argc = argc;

    ngx_argv = (char **) argv;

#else

    size_t     len;

    ngx_int_t  i;

    ngx_os_argv = (char **) argv;

    ngx_argc = argc;

    ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log);

    if (ngx_argv == NULL) {

        return NGX_ERROR;

    }

    for (i = 0; i < argc; i++) {

        len = ngx_strlen(argv[i]) + 1;

        ngx_argv[i] = ngx_alloc(len, cycle->log);

        if (ngx_argv[i] == NULL) {

            return NGX_ERROR;

        }

        (void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len);

    }

    ngx_argv[i] = NULL;

#endif

    ngx_os_environ = environ;

    return NGX_OK;

}

static ngx_int_t

‌ngx_process_options(ngx_cycle_t *cycle)

{

    u_char  *p;

    size_t   len;

    if (ngx_prefix) {

        len = ngx_strlen(ngx_prefix);

        p = ngx_prefix;

        if (len && !ngx_path_separator(p[len - 1])) {

            p = ngx_pnalloc(cycle->pool, len + 1);

            if (p == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(p, ngx_prefix, len);

            p[len++] = '/';

        }

        cycle->conf_prefix.len = len;

        cycle->conf_prefix.data = p;

        cycle->prefix.len = len;

        cycle->prefix.data = p;

    } else {

#ifndef NGX_PREFIX

        p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);

        if (p == NULL) {

            return NGX_ERROR;

        }

        if (ngx_getcwd(p, NGX_MAX_PATH) == 0) {

            ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed");

            return NGX_ERROR;

        }

        len = ngx_strlen(p);

        p[len++] = '/';

        cycle->conf_prefix.len = len;

        cycle->conf_prefix.data = p;

        cycle->prefix.len = len;

        cycle->prefix.data = p;

#else

#ifdef NGX_CONF_PREFIX

        ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);

#else

        ngx_str_set(&cycle->conf_prefix, NGX_PREFIX);

#endif

        ngx_str_set(&cycle->prefix, NGX_PREFIX);

#endif

    }

    if (ngx_conf_file) {

        cycle->conf_file.len = ngx_strlen(ngx_conf_file);

        cycle->conf_file.data = ngx_conf_file;

    } else {

        ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);

    }

    if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {

        return NGX_ERROR;

    }

    for (p = cycle->conf_file.data + cycle->conf_file.len - 1;

         p > cycle->conf_file.data;

         p--)

    {

        if (ngx_path_separator(*p)) {

            cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1;

            cycle->conf_prefix.data = ngx_cycle->conf_file.data;

            break;

        }

    }

    if (ngx_conf_params) {

        cycle->conf_param.len = ngx_strlen(ngx_conf_params);

        cycle->conf_param.data = ngx_conf_params;

    }

    if (ngx_test_config) {

        cycle->log->log_level = NGX_LOG_INFO;

    }

    return NGX_OK;

}

static void *

‌ngx_core_module_create_conf(ngx_cycle_t *cycle)

{

    ngx_core_conf_t  *ccf;

    ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));

    if (ccf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc()

     *

     *     ccf->pid = NULL;

     *     ccf->oldpid = NULL;

     *     ccf->priority = 0;

     *     ccf->cpu_affinity_n = 0;

     *     ccf->cpu_affinity = NULL;

     */

    ccf->daemon = NGX_CONF_UNSET;

    ccf->master = NGX_CONF_UNSET;

    ccf->timer_resolution = NGX_CONF_UNSET_MSEC;

    ccf->worker_processes = NGX_CONF_UNSET;

    ccf->debug_points = NGX_CONF_UNSET;

    ccf->rlimit_nofile = NGX_CONF_UNSET;

    ccf->rlimit_core = NGX_CONF_UNSET;

    ccf->rlimit_sigpending = NGX_CONF_UNSET;

    ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;

    ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;

#if (NGX_THREADS)

    ccf->worker_threads = NGX_CONF_UNSET;

    ccf->thread_stack_size = NGX_CONF_UNSET_SIZE;

#endif

    if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t))

        != NGX_OK)

    {

        return NULL;

    }

    return ccf;

}

static char *

‌ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_core_conf_t  *ccf = conf;

    ngx_conf_init_value(ccf->daemon, 1);

    ngx_conf_init_value(ccf->master, 1);

    ngx_conf_init_msec_value(ccf->timer_resolution, 0);

    ngx_conf_init_value(ccf->worker_processes, 1);

    ngx_conf_init_value(ccf->debug_points, 0);

#if (NGX_HAVE_CPU_AFFINITY)

    if (ccf->cpu_affinity_n

        && ccf->cpu_affinity_n != 1

        && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes)

    {

        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,

                      "the number of \"worker_processes\" is not equal to "

                      "the number of \"worker_cpu_affinity\" masks, "

                      "using last mask for remaining worker processes");

    }

#endif

#if (NGX_THREADS)

    ngx_conf_init_value(ccf->worker_threads, 0);

    ngx_threads_n = ccf->worker_threads;

    ngx_conf_init_size_value(ccf->thread_stack_size, 2 * 1024 * 1024);

#endif

    if (ccf->pid.len == 0) {

        ngx_str_set(&ccf->pid, NGX_PID_PATH);

    }

    if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);

    ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);

    if (ccf->oldpid.data == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len),

               NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT));

#if !(NGX_WIN32)

    if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) {

        struct group   *grp;

        struct passwd  *pwd;

        ngx_set_errno(0);

        pwd = getpwnam(NGX_USER);

        if (pwd == NULL) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "getpwnam(\"" NGX_USER "\") failed");

            return NGX_CONF_ERROR;

        }

        ccf->username = NGX_USER;

        ccf->user = pwd->pw_uid;

        ngx_set_errno(0);

        grp = getgrnam(NGX_GROUP);

        if (grp == NULL) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "getgrnam(\"" NGX_GROUP "\") failed");

            return NGX_CONF_ERROR;

        }

        ccf->group = grp->gr_gid;

    }

    if (ccf->lock_file.len == 0) {

        ngx_str_set(&ccf->lock_file, NGX_LOCK_PATH);

    }

    if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    {

    ngx_str_t  lock_file;

    lock_file = cycle->old_cycle->lock_file;

    if (lock_file.len) {

        lock_file.len--;

        if (ccf->lock_file.len != lock_file.len

            || ngx_strncmp(ccf->lock_file.data, lock_file.data, lock_file.len)

               != 0)

        {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                          "\"lock_file\" could not be changed, ignored");

        }

        cycle->lock_file.len = lock_file.len + 1;

        lock_file.len += sizeof(".accept");

        cycle->lock_file.data = ngx_pstrdup(cycle->pool, &lock_file);

        if (cycle->lock_file.data == NULL) {

            return NGX_CONF_ERROR;

        }

    } else {

        cycle->lock_file.len = ccf->lock_file.len + 1;

        cycle->lock_file.data = ngx_pnalloc(cycle->pool,

                                      ccf->lock_file.len + sizeof(".accept"));

        if (cycle->lock_file.data == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_memcpy(ngx_cpymem(cycle->lock_file.data, ccf->lock_file.data,

                              ccf->lock_file.len),

                   ".accept", sizeof(".accept"));

    }

    }

#endif

    return NGX_CONF_OK;

}

static char *

‌ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

#if (NGX_WIN32)

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"user\" is not supported, ignored");

    return NGX_CONF_OK;

#else

    ngx_core_conf_t  *ccf = conf;

    char             *group;

    struct passwd    *pwd;

    struct group     *grp;

    ngx_str_t        *value;

    if (ccf->user != (uid_t) NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    if (geteuid() != 0) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "the \"user\" directive makes sense only "

                           "if the master process runs "

                           "with super-user privileges, ignored");

        return NGX_CONF_OK;

    }

    value = (ngx_str_t *) cf->args->elts;

    ccf->username = (char *) value[1].data;

    ngx_set_errno(0);

    pwd = getpwnam((const char *) value[1].data);

    if (pwd == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                           "getpwnam(\"%s\") failed", value[1].data);

        return NGX_CONF_ERROR;

    }

    ccf->user = pwd->pw_uid;

    group = (char *) ((cf->args->nelts == 2) ? value[1].data : value[2].data);

    ngx_set_errno(0);

    grp = getgrnam(group);

    if (grp == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                           "getgrnam(\"%s\") failed", group);

        return NGX_CONF_ERROR;

    }

    ccf->group = grp->gr_gid;

    return NGX_CONF_OK;

#endif

}

static char *

‌ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_core_conf_t  *ccf = conf;

    ngx_str_t   *value, *var;

    ngx_uint_t   i;

    var = ngx_array_push(&ccf->env);

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    *var = value[1];

    for (i = 0; i < value[1].len; i++) {

        if (value[1].data[i] == '=') {

            var->len = i;

            return NGX_CONF_OK;

        }

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_core_conf_t  *ccf = conf;

    ngx_str_t        *value;

    ngx_uint_t        n, minus;

    if (ccf->priority != 0) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (value[1].data[0] == '-') {

        n = 1;

        minus = 1;

    } else if (value[1].data[0] == '+') {

        n = 1;

        minus = 0;

    } else {

        n = 0;

        minus = 0;

    }

    ccf->priority = ngx_atoi(&value[1].data[n], value[1].len - n);

    if (ccf->priority == NGX_ERROR) {

        return "invalid number";

    }

    if (minus) {

        ccf->priority = -ccf->priority;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

#if (NGX_HAVE_CPU_AFFINITY)

    ngx_core_conf_t  *ccf = conf;

    u_char            ch;

    uint64_t         *mask;

    ngx_str_t        *value;

    ngx_uint_t        i, n;

    if (ccf->cpu_affinity) {

        return "is duplicate";

    }

    mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(uint64_t));

    if (mask == NULL) {

        return NGX_CONF_ERROR;

    }

    ccf->cpu_affinity_n = cf->args->nelts - 1;

    ccf->cpu_affinity = mask;

    value = cf->args->elts;

    for (n = 1; n < cf->args->nelts; n++) {

        if (value[n].len > 64) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                         "\"worker_cpu_affinity\" supports up to 64 CPUs only");

            return NGX_CONF_ERROR;

        }

        mask[n - 1] = 0;

        for (i = 0; i < value[n].len; i++) {

            ch = value[n].data[i];

            if (ch == ' ') {

                continue;

            }

            mask[n - 1] <<= 1;

            if (ch == '0') {

                continue;

            }

            if (ch == '1') {

                mask[n - 1] |= 1;

                continue;

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                          "invalid character \"%c\" in \"worker_cpu_affinity\"",

                          ch);

            return NGX_CONF_ERROR;

        }

    }

#else

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"worker_cpu_affinity\" is not supported "

                       "on this platform, ignored");

#endif

    return NGX_CONF_OK;

}

uint64_t

‌ngx_get_cpu_affinity(ngx_uint_t n)

{

    ngx_core_conf_t  *ccf;

    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,

                                           ngx_core_module);

    if (ccf->cpu_affinity == NULL) {

        return 0;

    }

    if (ccf->cpu_affinity_n > n) {

        return ccf->cpu_affinity[n];

    }

    return ccf->cpu_affinity[ccf->cpu_affinity_n - 1];

}

static char *

‌ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_str_t        *value;

    ngx_core_conf_t  *ccf;

    ccf = (ngx_core_conf_t *) conf;

    if (ccf->worker_processes != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = (ngx_str_t *) cf->args->elts;

    if (ngx_strcmp(value[1].data, "auto") == 0) {

        ccf->worker_processes = ngx_ncpu;

        return NGX_CONF_OK;

    }

    ccf->worker_processes = ngx_atoi(value[1].data, value[1].len);

    if (ccf->worker_processes == NGX_ERROR) {

        return "invalid value";

    }

    return NGX_CONF_OK;

}

src/http/modules/perl/ngx_http_perl_module.h - nginx-1.7.9

Data types defined

nginx

ngx_http_perl_ctx_t

ngx_http_perl_var_t

Macros defined

_NGX_HTTP_PERL_MODULE_H_INCLUDED_

dTHXa

dTHXa

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_PERL_MODULE_H_INCLUDED_

‌#define _NGX_HTTP_PERL_MODULE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <nginx.h>

#include <EXTERN.h>

#include <perl.h>

‌typedef ngx_http_request_t   *nginx;

typedef struct {

    ngx_str_t                 filename;

    ngx_str_t                 redirect_uri;

    ngx_str_t                 redirect_args;

    SV                       *next;

    ngx_uint_t                done;       /* unsigned  done:1; */

    ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */

#if (NGX_HTTP_SSI)

    ngx_http_ssi_ctx_t       *ssi;

#endif

‌} ngx_http_perl_ctx_t;

typedef struct {

    ngx_uint_t    hash;

    ngx_str_t     name;

    ngx_str_t     value;

‌} ngx_http_perl_var_t;

extern ngx_module_t  ngx_http_perl_module;

/*

* workaround for "unused variable `Perl___notused'" warning

* when building with perl 5.6.1

*/

#ifndef PERL_IMPLICIT_CONTEXT

‌#undef  dTHXa

‌#define dTHXa(a)

#endif

extern void boot_DynaLoader(pTHX_ CV* cv);

void ngx_http_perl_handle_request(ngx_http_request_t *r);

void ngx_http_perl_sleep_handler(ngx_http_request_t *r);

#endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */

src/http/modules/perl/ngx_http_perl_module.c - nginx-1.7.9

Global variables defined

nginx_stash

ngx_http_perl_commands

ngx_http_perl_module

ngx_http_perl_module_ctx

ngx_http_perl_ssi_command

ngx_http_perl_ssi_params

ngx_null_name

ngx_perl_term

perl

Data types defined

ngx_http_perl_loc_conf_t

ngx_http_perl_main_conf_t

ngx_http_perl_variable_t

Functions defined

ngx_http_perl

ngx_http_perl_call_handler

ngx_http_perl_cleanup_perl

ngx_http_perl_create_interpreter

ngx_http_perl_create_loc_conf

ngx_http_perl_create_main_conf

ngx_http_perl_eval_anon_sub

ngx_http_perl_exit

ngx_http_perl_handle_request

ngx_http_perl_handler

ngx_http_perl_init_interpreter

ngx_http_perl_init_main_conf

ngx_http_perl_init_worker

ngx_http_perl_merge_loc_conf

ngx_http_perl_preconfiguration

ngx_http_perl_run_requires

ngx_http_perl_set

ngx_http_perl_sleep_handler

ngx_http_perl_ssi

ngx_http_perl_variable

ngx_http_perl_xs_init

Macros defined

NGX_HTTP_PERL_SSI_ARG

NGX_HTTP_PERL_SSI_SUB

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_http_perl_module.h>

typedef struct {

    PerlInterpreter   *perl;

    HV                *nginx;

    ngx_array_t       *modules;

    ngx_array_t       *requires;

‌} ngx_http_perl_main_conf_t;

typedef struct {

    SV                *sub;

    ngx_str_t          handler;

‌} ngx_http_perl_loc_conf_t;

typedef struct {

    SV                *sub;

    ngx_str_t          handler;

‌} ngx_http_perl_variable_t;

#if (NGX_HTTP_SSI)

static ngx_int_t ngx_http_perl_ssi(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);

#endif

static char *ngx_http_perl_init_interpreter(ngx_conf_t *cf,

    ngx_http_perl_main_conf_t *pmcf);

static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,

    ngx_http_perl_main_conf_t *pmcf);

static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,

    ngx_log_t *log);

static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,

    HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);

static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);

static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);

static void *ngx_http_perl_create_main_conf(ngx_conf_t *cf);

static char *ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf);

static void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

#if (NGX_HAVE_PERL_MULTIPLICITY)

static void ngx_http_perl_cleanup_perl(void *data);

#endif

static ngx_int_t ngx_http_perl_init_worker(ngx_cycle_t *cycle);

static void ngx_http_perl_exit(ngx_cycle_t *cycle);

‌static ngx_command_t  ngx_http_perl_commands[] = {

    { ngx_string("perl_modules"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_perl_main_conf_t, modules),

      NULL },

    { ngx_string("perl_require"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_perl_main_conf_t, requires),

      NULL },

    { ngx_string("perl"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,

      ngx_http_perl,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("perl_set"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,

      ngx_http_perl_set,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_perl_module_ctx = {

    ngx_http_perl_preconfiguration,        /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_perl_create_main_conf,        /* create main configuration */

    ngx_http_perl_init_main_conf,          /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_perl_create_loc_conf,         /* create location configuration */

    ngx_http_perl_merge_loc_conf           /* merge location configuration */

};

‌ngx_module_t  ngx_http_perl_module = {

    NGX_MODULE_V1,

    &ngx_http_perl_module_ctx,             /* module context */

    ngx_http_perl_commands,                /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    ngx_http_perl_init_worker,             /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    ngx_http_perl_exit,                    /* exit master */

    NGX_MODULE_V1_PADDING

};

#if (NGX_HTTP_SSI)

‌#define NGX_HTTP_PERL_SSI_SUB  0

‌#define NGX_HTTP_PERL_SSI_ARG  1

‌static ngx_http_ssi_param_t  ngx_http_perl_ssi_params[] = {

    { ngx_string("sub"), NGX_HTTP_PERL_SSI_SUB, 1, 0 },

    { ngx_string("arg"), NGX_HTTP_PERL_SSI_ARG, 0, 1 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_command_t  ngx_http_perl_ssi_command = {

    ngx_string("perl"), ngx_http_perl_ssi, ngx_http_perl_ssi_params, 0, 0, 1

};

#endif

‌static ngx_str_t         ngx_null_name = ngx_null_string;

‌static HV               *nginx_stash;

#if (NGX_HAVE_PERL_MULTIPLICITY)

‌static ngx_uint_t        ngx_perl_term;

#else

‌static PerlInterpreter  *perl;

#endif

static void

‌ngx_http_perl_xs_init(pTHX)

{

    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);

    nginx_stash = gv_stashpv("nginx", TRUE);

}

static ngx_int_t

‌ngx_http_perl_handler(ngx_http_request_t *r)

{

    r->main->count++;

    ngx_http_perl_handle_request(r);

    return NGX_DONE;

}

void

‌ngx_http_perl_handle_request(ngx_http_request_t *r)

{

    SV                         *sub;

    ngx_int_t                   rc;

    ngx_str_t                   uri, args, *handler;

    ngx_http_perl_ctx_t        *ctx;

    ngx_http_perl_loc_conf_t   *plcf;

    ngx_http_perl_main_conf_t  *pmcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);

    if (ctx == NULL) {

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));

        if (ctx == NULL) {

            ngx_http_finalize_request(r, NGX_ERROR);

            return;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);

    }

    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);

    {

    dTHXa(pmcf->perl);

    PERL_SET_CONTEXT(pmcf->perl);

    if (ctx->next == NULL) {

        plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);

        sub = plcf->sub;

        handler = &plcf->handler;

    } else {

        sub = ctx->next;

        handler = &ngx_null_name;

        ctx->next = NULL;

    }

    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sub, NULL, handler,

                                    NULL);

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "perl handler done: %i", rc);

    if (rc == NGX_DONE) {

        ngx_http_finalize_request(r, rc);

        return;

    }

    if (rc > 600) {

        rc = NGX_OK;

    }

    if (ctx->redirect_uri.len) {

        uri = ctx->redirect_uri;

        args = ctx->redirect_args;

    } else {

        uri.len = 0;

    }

    ctx->filename.data = NULL;

    ctx->redirect_uri.len = 0;

    if (ctx->done || ctx->next) {

        ngx_http_finalize_request(r, NGX_DONE);

        return;

    }

    if (uri.len) {

        ngx_http_internal_redirect(r, &uri, &args);

        ngx_http_finalize_request(r, NGX_DONE);

        return;

    }

    if (rc == NGX_OK || rc == NGX_HTTP_OK) {

        ngx_http_send_special(r, NGX_HTTP_LAST);

        ctx->done = 1;

    }

    ngx_http_finalize_request(r, rc);

}

void

‌ngx_http_perl_sleep_handler(ngx_http_request_t *r)

{

    ngx_event_t  *wev;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "perl sleep handler");

    wev = r->connection->write;

    if (wev->timedout) {

        wev->timedout = 0;

        ngx_http_perl_handle_request(r);

        return;

    }

    if (ngx_handle_write_event(wev, 0) != NGX_OK) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

    }

}

static ngx_int_t

‌ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_perl_variable_t *pv = (ngx_http_perl_variable_t *) data;

    ngx_int_t                   rc;

    ngx_str_t                   value;

    ngx_http_perl_ctx_t        *ctx;

    ngx_http_perl_main_conf_t  *pmcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "perl variable handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);

    if (ctx == NULL) {

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));

        if (ctx == NULL) {

            return NGX_ERROR;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);

    }

    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);

    value.data = NULL;

    {

    dTHXa(pmcf->perl);

    PERL_SET_CONTEXT(pmcf->perl);

    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL,

                                    &pv->handler, &value);

    }

    if (value.data) {

        v->len = value.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = value.data;

    } else {

        v->not_found = 1;

    }

    ctx->filename.data = NULL;

    ctx->redirect_uri.len = 0;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "perl variable done");

    return rc;

}

#if (NGX_HTTP_SSI)

static ngx_int_t

‌ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,

    ngx_str_t **params)

{

    SV                         *sv, **asv;

    ngx_int_t                   rc;

    ngx_str_t                  *handler, **args;

    ngx_uint_t                  i;

    ngx_http_perl_ctx_t        *ctx;

    ngx_http_perl_main_conf_t  *pmcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "perl ssi handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);

    if (ctx == NULL) {

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));

        if (ctx == NULL) {

            return NGX_ERROR;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);

    }

    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);

    ctx->ssi = ssi_ctx;

    handler = params[NGX_HTTP_PERL_SSI_SUB];

    handler->data[handler->len] = '\0';

    {

    dTHXa(pmcf->perl);

    PERL_SET_CONTEXT(pmcf->perl);

#if 0

    /* the code is disabled to force the precompiled perl code using only */

    ngx_http_perl_eval_anon_sub(aTHX_ handler, &sv);

    if (sv == &PL_sv_undef) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "eval_pv(\"%V\") failed", handler);

        return NGX_ERROR;

    }

    if (sv == NULL) {

        sv = newSVpvn((char *) handler->data, handler->len);

    }

#endif

    sv = newSVpvn((char *) handler->data, handler->len);

    args = &params[NGX_HTTP_PERL_SSI_ARG];

    if (args) {

        for (i = 0; args[i]; i++) { /* void */ }

        asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *));

        if (asv == NULL) {

            SvREFCNT_dec(sv);

            return NGX_ERROR;

        }

        asv[0] = (SV *) (uintptr_t) i;

        for (i = 0; args[i]; i++) {

            asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len);

        }

    } else {

        asv = NULL;

    }

    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler,

                                    NULL);

    SvREFCNT_dec(sv);

    }

    ctx->filename.data = NULL;

    ctx->redirect_uri.len = 0;

    ctx->ssi = NULL;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl ssi done");

    return rc;

}

#endif

static char *

‌ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)

{

    ngx_str_t           *m;

    ngx_uint_t           i;

#if (NGX_HAVE_PERL_MULTIPLICITY)

    ngx_pool_cleanup_t  *cln;

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_CONF_ERROR;

    }

#endif

#ifdef NGX_PERL_MODULES

    if (pmcf->modules == NGX_CONF_UNSET_PTR) {

        pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));

        if (pmcf->modules == NULL) {

            return NGX_CONF_ERROR;

        }

        m = ngx_array_push(pmcf->modules);

        if (m == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_str_set(m, NGX_PERL_MODULES);

    }

#endif

    if (pmcf->modules != NGX_CONF_UNSET_PTR) {

        m = pmcf->modules->elts;

        for (i = 0; i < pmcf->modules->nelts; i++) {

            if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    }

#if !(NGX_HAVE_PERL_MULTIPLICITY)

    if (perl) {

        if (ngx_set_environment(cf->cycle, NULL) == NULL) {

            return NGX_CONF_ERROR;

        }

        if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

        pmcf->perl = perl;

        pmcf->nginx = nginx_stash;

        return NGX_CONF_OK;

    }

#endif

    if (nginx_stash == NULL) {

        PERL_SYS_INIT(&ngx_argc, &ngx_argv);

    }

    pmcf->perl = ngx_http_perl_create_interpreter(cf, pmcf);

    if (pmcf->perl == NULL) {

        return NGX_CONF_ERROR;

    }

    pmcf->nginx = nginx_stash;

#if (NGX_HAVE_PERL_MULTIPLICITY)

    cln->handler = ngx_http_perl_cleanup_perl;

    cln->data = pmcf->perl;

#else

    perl = pmcf->perl;

#endif

    return NGX_CONF_OK;

}

static PerlInterpreter *

‌ngx_http_perl_create_interpreter(ngx_conf_t *cf,

    ngx_http_perl_main_conf_t *pmcf)

{

    int                n;

    STRLEN             len;

    SV                *sv;

    char              *ver, **embedding;

    ngx_str_t         *m;

    ngx_uint_t         i;

    PerlInterpreter   *perl;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "create perl interpreter");

    if (ngx_set_environment(cf->cycle, NULL) == NULL) {

        return NULL;

    }

    perl = perl_alloc();

    if (perl == NULL) {

        ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_alloc() failed");

        return NULL;

    }

    {

    dTHXa(perl);

    PERL_SET_CONTEXT(perl);

    perl_construct(perl);

#ifdef PERL_EXIT_DESTRUCT_END

    PL_exit_flags |= PERL_EXIT_DESTRUCT_END;

#endif

    n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0;

    embedding = ngx_palloc(cf->pool, (5 + n) * sizeof(char *));

    if (embedding == NULL) {

        goto fail;

    }

    embedding[0] = "";

    if (n++) {

        m = pmcf->modules->elts;

        for (i = 0; i < pmcf->modules->nelts; i++) {

            embedding[2 * i + 1] = "-I";

            embedding[2 * i + 2] = (char *) m[i].data;

        }

    }

    embedding[n++] = "-Mnginx";

    embedding[n++] = "-e";

    embedding[n++] = "0";

    embedding[n] = NULL;

    n = perl_parse(perl, ngx_http_perl_xs_init, n, embedding, NULL);

    if (n != 0) {

        ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_parse() failed: %d", n);

        goto fail;

    }

    sv = get_sv("nginx::VERSION", FALSE);

    ver = SvPV(sv, len);

    if (ngx_strcmp(ver, NGINX_VERSION) != 0) {

        ngx_log_error(NGX_LOG_ALERT, cf->log, 0,

                      "version " NGINX_VERSION " of nginx.pm is required, "

                      "but %s was found", ver);

        goto fail;

    }

    if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) {

        goto fail;

    }

    }

    return perl;

fail:

    (void) perl_destruct(perl);

    perl_free(perl);

    return NULL;

}

static ngx_int_t

‌ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)

{

    u_char      *err;

    STRLEN       len;

    ngx_str_t   *script;

    ngx_uint_t   i;

    if (requires == NGX_CONF_UNSET_PTR) {

        return NGX_OK;

    }

    script = requires->elts;

    for (i = 0; i < requires->nelts; i++) {

        require_pv((char *) script[i].data);

        if (SvTRUE(ERRSV)) {

            err = (u_char *) SvPV(ERRSV, len);

            while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }

            ngx_log_error(NGX_LOG_EMERG, log, 0,

                          "require_pv(\"%s\") failed: \"%*s\"",

                          script[i].data, len + 1, err);

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,

    SV **args, ngx_str_t *handler, ngx_str_t *rv)

{

    SV                *sv;

    int                n, status;

    char              *line;

    u_char            *err;

    STRLEN             len, n_a;

    ngx_uint_t         i;

    ngx_connection_t  *c;

    dSP;

    status = 0;

    ENTER;

    SAVETMPS;

    PUSHMARK(sp);

    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx));

    XPUSHs(sv);

    if (args) {

        EXTEND(sp, (intptr_t) args[0]);

        for (i = 1; i <= (uintptr_t) args[0]; i++) {

            PUSHs(sv_2mortal(args[i]));

        }

    }

    PUTBACK;

    c = r->connection;

    n = call_sv(sub, G_EVAL);

    SPAGAIN;

    if (n) {

        if (rv == NULL) {

            status = POPi;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "call_sv: %d", status);

        } else {

            line = SvPVx(POPs, n_a);

            rv->len = n_a;

            rv->data = ngx_pnalloc(r->pool, n_a);

            if (rv->data == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(rv->data, line, n_a);

        }

    }

    PUTBACK;

    FREETMPS;

    LEAVE;

    /* check $@ */

    if (SvTRUE(ERRSV)) {

        err = (u_char *) SvPV(ERRSV, len);

        while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }

        ngx_log_error(NGX_LOG_ERR, c->log, 0,

                      "call_sv(\"%V\") failed: \"%*s\"", handler, len + 1, err);

        if (rv) {

            return NGX_ERROR;

        }

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (n != 1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "call_sv(\"%V\") returned %d results", handler, n);

        status = NGX_OK;

    }

    if (rv) {

        return NGX_OK;

    }

    return (ngx_int_t) status;

}

static void

‌ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv)

{

    u_char  *p;

    for (p = handler->data; *p; p++) {

        if (*p != ' ' && *p != '\t' && *p != CR && *p != LF) {

            break;

        }

    }

    if (ngx_strncmp(p, "sub ", 4) == 0

        || ngx_strncmp(p, "sub{", 4) == 0

        || ngx_strncmp(p, "use ", 4) == 0)

    {

        *sv = eval_pv((char *) p, FALSE);

        /* eval_pv() does not set ERRSV on failure */

        return;

    }

    *sv = NULL;

}

static void *

‌ngx_http_perl_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_perl_main_conf_t  *pmcf;

    pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t));

    if (pmcf == NULL) {

        return NULL;

    }

    pmcf->modules = NGX_CONF_UNSET_PTR;

    pmcf->requires = NGX_CONF_UNSET_PTR;

    return pmcf;

}

static char *

‌ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_perl_main_conf_t *pmcf = conf;

    if (pmcf->perl == NULL) {

        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

#if (NGX_HAVE_PERL_MULTIPLICITY)

static void

‌ngx_http_perl_cleanup_perl(void *data)

{

    PerlInterpreter  *perl = data;

    PERL_SET_CONTEXT(perl);

    (void) perl_destruct(perl);

    perl_free(perl);

    if (ngx_perl_term) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "perl term");

        PERL_SYS_TERM();

    }

}

#endif

static ngx_int_t

‌ngx_http_perl_preconfiguration(ngx_conf_t *cf)

{

#if (NGX_HTTP_SSI)

    ngx_int_t                  rc;

    ngx_http_ssi_main_conf_t  *smcf;

    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);

    rc = ngx_hash_add_key(&smcf->commands, &ngx_http_perl_ssi_command.name,

                          &ngx_http_perl_ssi_command, NGX_HASH_READONLY_KEY);

    if (rc != NGX_OK) {

        if (rc == NGX_BUSY) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "conflicting SSI command \"%V\"",

                               &ngx_http_perl_ssi_command.name);

        }

        return NGX_ERROR;

    }

#endif

    return NGX_OK;

}

static void *

‌ngx_http_perl_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_perl_loc_conf_t *plcf;

    plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t));

    if (plcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     plcf->handler = { 0, NULL };

     */

    return plcf;

}

static char *

‌ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_perl_loc_conf_t *prev = parent;

    ngx_http_perl_loc_conf_t *conf = child;

    if (conf->sub == NULL) {

        conf->sub = prev->sub;

        conf->handler = prev->handler;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_perl_loc_conf_t *plcf = conf;

    ngx_str_t                  *value;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_perl_main_conf_t  *pmcf;

    value = cf->args->elts;

    if (plcf->handler.data) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "duplicate perl handler \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);

    if (pmcf->perl == NULL) {

        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

    }

    plcf->handler = value[1];

    {

    dTHXa(pmcf->perl);

    PERL_SET_CONTEXT(pmcf->perl);

    ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub);

    if (plcf->sub == &PL_sv_undef) {

        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,

                           "eval_pv(\"%V\") failed", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (plcf->sub == NULL) {

        plcf->sub = newSVpvn((char *) value[1].data, value[1].len);

    }

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_perl_handler;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_int_t                   index;

    ngx_str_t                  *value;

    ngx_http_variable_t        *v;

    ngx_http_perl_variable_t   *pv;

    ngx_http_perl_main_conf_t  *pmcf;

    value = cf->args->elts;

    if (value[1].data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    value[1].len--;

    value[1].data++;

    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);

    if (v == NULL) {

        return NGX_CONF_ERROR;

    }

    pv = ngx_palloc(cf->pool, sizeof(ngx_http_perl_variable_t));

    if (pv == NULL) {

        return NGX_CONF_ERROR;

    }

    index = ngx_http_get_variable_index(cf, &value[1]);

    if (index == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);

    if (pmcf->perl == NULL) {

        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

    }

    pv->handler = value[2];

    {

    dTHXa(pmcf->perl);

    PERL_SET_CONTEXT(pmcf->perl);

    ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub);

    if (pv->sub == &PL_sv_undef) {

        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,

                           "eval_pv(\"%V\") failed", &value[2]);

        return NGX_CONF_ERROR;

    }

    if (pv->sub == NULL) {

        pv->sub = newSVpvn((char *) value[2].data, value[2].len);

    }

    }

    v->get_handler = ngx_http_perl_variable;

    v->data = (uintptr_t) pv;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_perl_init_worker(ngx_cycle_t *cycle)

{

    ngx_http_perl_main_conf_t  *pmcf;

    pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);

    if (pmcf) {

        dTHXa(pmcf->perl);

        PERL_SET_CONTEXT(pmcf->perl);

        /* set worker's $$ */

        sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid);

    }

    return NGX_OK;

}

static void

‌ngx_http_perl_exit(ngx_cycle_t *cycle)

{

#if (NGX_HAVE_PERL_MULTIPLICITY)

    /*

     * the master exit hook is run before global pool cleanup,

     * therefore just set flag here

     */

    ngx_perl_term = 1;

#else

    if (nginx_stash) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term");

        (void) perl_destruct(perl);

        perl_free(perl);

        PERL_SYS_TERM();

    }

#endif

}

src/core/ngx_array.h - nginx-1.7.9

Data types defined

ngx_array_t

Functions defined

ngx_array_init

Macros defined

_NGX_ARRAY_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_ARRAY_H_INCLUDED_

‌#define _NGX_ARRAY_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    void        *elts;

    ngx_uint_t   nelts;

    size_t       size;

    ngx_uint_t   nalloc;

    ngx_pool_t  *pool;

‌} ngx_array_t;

ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);

void ngx_array_destroy(ngx_array_t *a);

void *ngx_array_push(ngx_array_t *a);

void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);

static ngx_inline ngx_int_t

‌ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)

{

    /*

     * set "array->nelts" before "array->elts", otherwise MSVC thinks

     * that "array->nelts" may be used without having been initialized

     */

    array->nelts = 0;

    array->size = size;

    array->nalloc = n;

    array->pool = pool;

    array->elts = ngx_palloc(pool, n * size);

    if (array->elts == NULL) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

#endif /* _NGX_ARRAY_H_INCLUDED_ */

src/core/ngx_config.h - nginx-1.7.9

Data types defined

ngx_flag_t

ngx_int_t

ngx_uint_t

Macros defined

INADDR_NONE

NGX_ALIGNMENT

NGX_CHANGEBIN_SIGNAL

NGX_CHANGEBIN_SIGNAL

NGX_HAVE_SO_SNDLOWAT

NGX_INT32_LEN

NGX_INT64_LEN

NGX_INT_T_LEN

NGX_INT_T_LEN

NGX_INVALID_ARRAY_INDEX

NGX_MAXHOSTNAMELEN

NGX_MAXHOSTNAMELEN

NGX_MAX_INT32_VALUE

NGX_MAX_UINT32_VALUE

NGX_MAX_UINT32_VALUE

NGX_NOACCEPT_SIGNAL

NGX_RECONFIGURE_SIGNAL

NGX_REOPEN_SIGNAL

NGX_REOPEN_SIGNAL

NGX_SHUTDOWN_SIGNAL

NGX_TERMINATE_SIGNAL

_NGX_CONFIG_H_INCLUDED_

__FreeBSD__

__FreeBSD_version

ngx_abort

ngx_align

ngx_align_ptr

ngx_cdecl

ngx_inline

ngx_libc_cdecl

ngx_random

ngx_signal_helper

ngx_signal_value

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CONFIG_H_INCLUDED_

‌#define _NGX_CONFIG_H_INCLUDED_

#include <ngx_auto_headers.h>

#if defined __DragonFly__ && !defined __FreeBSD__

‌#define __FreeBSD__        4

‌#define __FreeBSD_version  480101

#endif

#if (NGX_FREEBSD)

#include <ngx_freebsd_config.h>

#elif (NGX_LINUX)

#include <ngx_linux_config.h>

#elif (NGX_SOLARIS)

#include <ngx_solaris_config.h>

#elif (NGX_DARWIN)

#include <ngx_darwin_config.h>

#elif (NGX_WIN32)

#include <ngx_win32_config.h>

#else /* POSIX */

#include <ngx_posix_config.h>

#endif

#ifndef NGX_HAVE_SO_SNDLOWAT

‌#define NGX_HAVE_SO_SNDLOWAT     1

#endif

#if !(NGX_WIN32)

‌#define ngx_signal_helper(n)     SIG##n

‌#define ngx_signal_value(n)      ngx_signal_helper(n)

‌#define ngx_random               random

/* TODO: #ifndef */

‌#define NGX_SHUTDOWN_SIGNAL      QUIT

‌#define NGX_TERMINATE_SIGNAL     TERM

‌#define NGX_NOACCEPT_SIGNAL      WINCH

‌#define NGX_RECONFIGURE_SIGNAL   HUP

#if (NGX_LINUXTHREADS)

‌#define NGX_REOPEN_SIGNAL        INFO

‌#define NGX_CHANGEBIN_SIGNAL     XCPU

#else

‌#define NGX_REOPEN_SIGNAL        USR1

‌#define NGX_CHANGEBIN_SIGNAL     USR2

#endif

‌#define ngx_cdecl

‌#define ngx_libc_cdecl

#endif

‌typedef intptr_t        ngx_int_t;

‌typedef uintptr_t       ngx_uint_t;

‌typedef intptr_t        ngx_flag_t;

‌#define NGX_INT32_LEN   (sizeof("-2147483648") - 1)

‌#define NGX_INT64_LEN   (sizeof("-9223372036854775808") - 1)

#if (NGX_PTR_SIZE == 4)

‌#define NGX_INT_T_LEN   NGX_INT32_LEN

#else

‌#define NGX_INT_T_LEN   NGX_INT64_LEN

#endif

#ifndef NGX_ALIGNMENT

‌#define NGX_ALIGNMENT   sizeof(unsigned long)    /* platform word */

#endif

‌#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))

‌#define ngx_align_ptr(p, a)                                                   \

    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

‌#define ngx_abort       abort

/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */

‌#define NGX_INVALID_ARRAY_INDEX 0x80000000

/* TODO: auto_conf: ngx_inline   inline __inline __inline__ */

#ifndef ngx_inline

‌#define ngx_inline      inline

#endif

#ifndef INADDR_NONE  /* Solaris */

‌#define INADDR_NONE  ((unsigned int) -1)

#endif

#ifdef MAXHOSTNAMELEN

‌#define NGX_MAXHOSTNAMELEN  MAXHOSTNAMELEN

#else

‌#define NGX_MAXHOSTNAMELEN  256

#endif

#if ((__GNU__ == 2) && (__GNUC_MINOR__ < 8))

‌#define NGX_MAX_UINT32_VALUE  (uint32_t) 0xffffffffLL

#else

‌#define NGX_MAX_UINT32_VALUE  (uint32_t) 0xffffffff

#endif

‌#define NGX_MAX_INT32_VALUE   (uint32_t) 0x7fffffff

#endif /* _NGX_CONFIG_H_INCLUDED_ */

src/os/unix/ngx_linux_config.h - nginx-1.7.9

Data types defined

ngx_aiocb_t

Macros defined

NGX_HAVE_INHERITED_NONBLOCK

NGX_HAVE_OS_SPECIFIC_INIT

NGX_HAVE_SO_SNDLOWAT

NGX_LISTEN_BACKLOG

NGX_SENDFILE_LIMIT

_FILE_OFFSET_BITS

_GNU_SOURCE

_NGX_LINUX_CONFIG_H_INCLUDED_

ngx_debug_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_LINUX_CONFIG_H_INCLUDED_

‌#define _NGX_LINUX_CONFIG_H_INCLUDED_

#ifndef _GNU_SOURCE

‌#define _GNU_SOURCE             /* pread(), pwrite(), gethostname() */

#endif

‌#define _FILE_OFFSET_BITS  64

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>

#include <stdarg.h>

#include <stddef.h>             /* offsetof() */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <pwd.h>

#include <grp.h>

#include <dirent.h>

#include <glob.h>

#include <sys/vfs.h>            /* statfs() */

#include <sys/uio.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/mman.h>

#include <sys/resource.h>

#include <sched.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>        /* TCP_NODELAY, TCP_CORK */

#include <arpa/inet.h>

#include <netdb.h>

#include <sys/un.h>

#include <time.h>               /* tzset() */

#include <malloc.h>             /* memalign() */

#include <limits.h>             /* IOV_MAX */

#include <sys/ioctl.h>

#include <crypt.h>

#include <sys/utsname.h>        /* uname() */

#include <ngx_auto_config.h>

#if (NGX_HAVE_POSIX_SEM)

#include <semaphore.h>

#endif

#if (NGX_HAVE_SYS_PRCTL_H)

#include <sys/prctl.h>

#endif

#if (NGX_HAVE_SENDFILE64)

#include <sys/sendfile.h>

#else

extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);

‌#define NGX_SENDFILE_LIMIT  0x80000000

#endif

#if (NGX_HAVE_POLL)

#include <poll.h>

#endif

#if (NGX_HAVE_RTSIG)

#include <poll.h>

#include <sys/sysctl.h>

#endif

#if (NGX_HAVE_EPOLL)

#include <sys/epoll.h>

#endif

#if (NGX_HAVE_FILE_AIO)

#if (NGX_HAVE_SYS_EVENTFD_H)

#include <sys/eventfd.h>

#endif

#include <sys/syscall.h>

#include <linux/aio_abi.h>

‌typedef struct iocb  ngx_aiocb_t;

#endif

‌#define NGX_LISTEN_BACKLOG        511

#ifndef NGX_HAVE_SO_SNDLOWAT

/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */

‌#define NGX_HAVE_SO_SNDLOWAT         0

#endif

#ifndef NGX_HAVE_INHERITED_NONBLOCK

‌#define NGX_HAVE_INHERITED_NONBLOCK  0

#endif

‌#define NGX_HAVE_OS_SPECIFIC_INIT    1

‌#define ngx_debug_init()

extern char **environ;

#endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */

src/os/unix/ngx_posix_config.h - nginx-1.7.9

Data types defined

ngx_aiocb_t

Macros defined

CMSG_DATA

CMSG_DATA

CMSG_LEN

CMSG_LEN

CMSG_SPACE

CMSG_SPACE

IOV_MAX

NGX_BROKEN_SCM_RIGHTS

NGX_LISTEN_BACKLOG

_HPUX_ALT_XOPEN_SOCKET_API

_NGX_POSIX_CONFIG_H_INCLUDED_

_REENTRANT

_XOPEN_SOURCE

_XOPEN_SOURCE_EXTENDED

ngx_debug_init

timezonevar

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_POSIX_CONFIG_H_INCLUDED_

‌#define _NGX_POSIX_CONFIG_H_INCLUDED_

#if (NGX_HPUX)

‌#define _XOPEN_SOURCE

‌#define _XOPEN_SOURCE_EXTENDED  1

‌#define _HPUX_ALT_XOPEN_SOCKET_API

#endif

#if (NGX_TRU64)

‌#define _REENTRANT

#endif

#ifdef __CYGWIN__

‌#define timezonevar             /* timezone is variable */

‌#define NGX_BROKEN_SCM_RIGHTS   1

#endif

#include <sys/types.h>

#include <sys/time.h>

#if (NGX_HAVE_UNISTD_H)

#include <unistd.h>

#endif

#if (NGX_HAVE_INTTYPES_H)

#include <inttypes.h>

#endif

#include <stdarg.h>

#include <stddef.h>             /* offsetof() */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <pwd.h>

#include <grp.h>

#include <dirent.h>

#include <glob.h>

#include <time.h>

#if (NGX_HAVE_SYS_PARAM_H)

#include <sys/param.h>          /* statfs() */

#endif

#if (NGX_HAVE_SYS_MOUNT_H)

#include <sys/mount.h>          /* statfs() */

#endif

#if (NGX_HAVE_SYS_STATVFS_H)

#include <sys/statvfs.h>        /* statvfs() */

#endif

#if (NGX_HAVE_SYS_FILIO_H)

#include <sys/filio.h>          /* FIONBIO */

#endif

#include <sys/ioctl.h>          /* FIONBIO */

#include <sys/uio.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/mman.h>

#include <sys/resource.h>

#include <sched.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>        /* TCP_NODELAY */

#include <arpa/inet.h>

#include <netdb.h>

#include <sys/un.h>

#if (NGX_HAVE_LIMITS_H)

#include <limits.h>             /* IOV_MAX */

#endif

#ifdef __CYGWIN__

#include <malloc.h>             /* memalign() */

#endif

#if (NGX_HAVE_CRYPT_H)

#include <crypt.h>

#endif

#ifndef IOV_MAX

‌#define IOV_MAX   16

#endif

#include <ngx_auto_config.h>

#if (NGX_HAVE_POSIX_SEM)

#include <semaphore.h>

#endif

#if (NGX_HAVE_POLL)

#include <poll.h>

#endif

#if (NGX_HAVE_KQUEUE)

#include <sys/event.h>

#endif

#if (NGX_HAVE_DEVPOLL)

#include <sys/ioctl.h>

#include <sys/devpoll.h>

#endif

#if (NGX_HAVE_FILE_AIO)

#include <aio.h>

‌typedef struct aiocb  ngx_aiocb_t;

#endif

‌#define NGX_LISTEN_BACKLOG  511

‌#define ngx_debug_init()

#if (__FreeBSD__) && (__FreeBSD_version < 400017)

#include <sys/param.h>          /* ALIGN() */

/*

* FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()

*/

‌#undef  CMSG_SPACE

‌#define CMSG_SPACE(l)       (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))

‌#undef  CMSG_LEN

‌#define CMSG_LEN(l)         (ALIGN(sizeof(struct cmsghdr)) + (l))

‌#undef  CMSG_DATA

‌#define CMSG_DATA(cmsg)     ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))

#endif

extern char **environ;

#endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */

src/os/unix/ngx_solaris_config.h - nginx-1.7.9

Macros defined

NGX_ALIGNMENT

NGX_HAVE_INHERITED_NONBLOCK

NGX_HAVE_OS_SPECIFIC_INIT

NGX_HAVE_SO_SNDLOWAT

NGX_LISTEN_BACKLOG

_FILE_OFFSET_BITS

_NGX_SOLARIS_CONFIG_H_INCLUDED_

_REENTRANT

ngx_debug_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SOLARIS_CONFIG_H_INCLUDED_

‌#define _NGX_SOLARIS_CONFIG_H_INCLUDED_

#ifndef _REENTRANT

‌#define _REENTRANT

#endif

‌#define _FILE_OFFSET_BITS  64   /* must be before <sys/types.h> */

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>

#include <stdarg.h>

#include <stddef.h>             /* offsetof() */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <pwd.h>

#include <grp.h>

#include <dirent.h>

#include <glob.h>

#include <time.h>

#include <sys/statvfs.h>        /* statvfs() */

#include <sys/filio.h>          /* FIONBIO */

#include <sys/uio.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/mman.h>

#include <sys/resource.h>

#include <sched.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>        /* TCP_NODELAY */

#include <arpa/inet.h>

#include <netdb.h>

#include <sys/un.h>

#include <sys/systeminfo.h>

#include <limits.h>             /* IOV_MAX */

#include <inttypes.h>

#include <crypt.h>

‌#define NGX_ALIGNMENT  _MAX_ALIGNMENT

#include <ngx_auto_config.h>

#if (NGX_HAVE_POSIX_SEM)

#include <semaphore.h>

#endif

#if (NGX_HAVE_POLL)

#include <poll.h>

#endif

#if (NGX_HAVE_DEVPOLL)

#include <sys/ioctl.h>

#include <sys/devpoll.h>

#endif

#if (NGX_HAVE_EVENTPORT)

#include <port.h>

#endif

#if (NGX_HAVE_SENDFILE)

#include <sys/sendfile.h>

#endif

‌#define NGX_LISTEN_BACKLOG           511

#ifndef NGX_HAVE_INHERITED_NONBLOCK

‌#define NGX_HAVE_INHERITED_NONBLOCK  1

#endif

#ifndef NGX_HAVE_SO_SNDLOWAT

/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */

‌#define NGX_HAVE_SO_SNDLOWAT         0

#endif

‌#define NGX_HAVE_OS_SPECIFIC_INIT    1

‌#define ngx_debug_init()

extern char **environ;

#endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */

src/core/ngx_core.h - nginx-1.7.9

Data types defined

ngx_chain_t

ngx_command_t

ngx_conf_t

ngx_connection_handler_pt

ngx_connection_t

ngx_cycle_t

ngx_event_aio_t

ngx_event_handler_pt

ngx_event_t

ngx_file_t

ngx_log_t

ngx_module_t

ngx_open_file_t

ngx_pool_t

Macros defined

CR

CRLF

LF

NGX_ABORT

NGX_AGAIN

NGX_BUSY

NGX_DECLINED

NGX_DISABLE_SYMLINKS_NOTOWNER

NGX_DISABLE_SYMLINKS_OFF

NGX_DISABLE_SYMLINKS_ON

NGX_DONE

NGX_ERROR

NGX_OK

_NGX_CORE_H_INCLUDED_

ngx_abs

ngx_max

ngx_min

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CORE_H_INCLUDED_

‌#define _NGX_CORE_H_INCLUDED_

‌typedef struct ngx_module_s      ngx_module_t;

‌typedef struct ngx_conf_s        ngx_conf_t;

‌typedef struct ngx_cycle_s       ngx_cycle_t;

‌typedef struct ngx_pool_s        ngx_pool_t;

‌typedef struct ngx_chain_s       ngx_chain_t;

‌typedef struct ngx_log_s         ngx_log_t;

‌typedef struct ngx_open_file_s   ngx_open_file_t;

‌typedef struct ngx_command_s     ngx_command_t;

‌typedef struct ngx_file_s        ngx_file_t;

‌typedef struct ngx_event_s       ngx_event_t;

‌typedef struct ngx_event_aio_s   ngx_event_aio_t;

‌typedef struct ngx_connection_s  ngx_connection_t;

‌typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);

‌typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);

‌#define  NGX_OK          0

‌#define  NGX_ERROR      -1

‌#define  NGX_AGAIN      -2

‌#define  NGX_BUSY       -3

‌#define  NGX_DONE       -4

‌#define  NGX_DECLINED   -5

‌#define  NGX_ABORT      -6

#include <ngx_errno.h>

#include <ngx_atomic.h>

#include <ngx_thread.h>

#include <ngx_rbtree.h>

#include <ngx_time.h>

#include <ngx_socket.h>

#include <ngx_string.h>

#include <ngx_files.h>

#include <ngx_shmem.h>

#include <ngx_process.h>

#include <ngx_user.h>

#include <ngx_parse.h>

#include <ngx_log.h>

#include <ngx_alloc.h>

#include <ngx_palloc.h>

#include <ngx_buf.h>

#include <ngx_queue.h>

#include <ngx_array.h>

#include <ngx_list.h>

#include <ngx_hash.h>

#include <ngx_file.h>

#include <ngx_crc.h>

#include <ngx_crc32.h>

#include <ngx_murmurhash.h>

#if (NGX_PCRE)

#include <ngx_regex.h>

#endif

#include <ngx_radix_tree.h>

#include <ngx_times.h>

#include <ngx_shmtx.h>

#include <ngx_slab.h>

#include <ngx_inet.h>

#include <ngx_cycle.h>

#include <ngx_resolver.h>

#if (NGX_OPENSSL)

#include <ngx_event_openssl.h>

#endif

#include <ngx_process_cycle.h>

#include <ngx_conf_file.h>

#include <ngx_open_file_cache.h>

#include <ngx_os.h>

#include <ngx_connection.h>

#include <ngx_syslog.h>

#include <ngx_proxy_protocol.h>

‌#define LF     (u_char) '\n'

‌#define CR     (u_char) '\r'

‌#define CRLF   "\r\n"

‌#define ngx_abs(value)       (((value) >= 0) ? (value) : - (value))

‌#define ngx_max(val1, val2)  ((val1 < val2) ? (val2) : (val1))

‌#define ngx_min(val1, val2)  ((val1 > val2) ? (val2) : (val1))

void ngx_cpuinfo(void);

#if (NGX_HAVE_OPENAT)

‌#define NGX_DISABLE_SYMLINKS_OFF        0

‌#define NGX_DISABLE_SYMLINKS_ON         1

‌#define NGX_DISABLE_SYMLINKS_NOTOWNER   2

#endif

#endif /* _NGX_CORE_H_INCLUDED_ */

src/core/ngx_conf_file.h - nginx-1.7.9

Data types defined

ngx_command_s

ngx_conf_bitmask_t

ngx_conf_deprecated_t

ngx_conf_enum_t

ngx_conf_file_t

ngx_conf_handler_pt

ngx_conf_num_bounds_t

ngx_conf_post_handler_pt

ngx_conf_post_t

ngx_conf_s

ngx_core_module_t

ngx_module_s

ngx_open_file_s

Macros defined

NGX_ANY_CONF

NGX_CONF_1MORE

NGX_CONF_2MORE

NGX_CONF_ANY

NGX_CONF_ARGS_NUMBER

NGX_CONF_BITMASK_SET

NGX_CONF_BLOCK

NGX_CONF_BLOCK_DONE

NGX_CONF_BLOCK_START

NGX_CONF_ERROR

NGX_CONF_FILE_DONE

NGX_CONF_FLAG

NGX_CONF_MAX_ARGS

NGX_CONF_MODULE

NGX_CONF_MULTI

NGX_CONF_NOARGS

NGX_CONF_OK

NGX_CONF_TAKE1

NGX_CONF_TAKE12

NGX_CONF_TAKE123

NGX_CONF_TAKE1234

NGX_CONF_TAKE13

NGX_CONF_TAKE2

NGX_CONF_TAKE23

NGX_CONF_TAKE3

NGX_CONF_TAKE4

NGX_CONF_TAKE5

NGX_CONF_TAKE6

NGX_CONF_TAKE7

NGX_CONF_UNSET

NGX_CONF_UNSET_MSEC

NGX_CONF_UNSET_PTR

NGX_CONF_UNSET_SIZE

NGX_CONF_UNSET_UINT

NGX_CORE_MODULE

NGX_DIRECT_CONF

NGX_MAIN_CONF

NGX_MAX_CONF_ERRSTR

NGX_MODULE_V1

NGX_MODULE_V1_PADDING

_NGX_CONF_FILE_H_INCLUDED_

ngx_conf_init_msec_value

ngx_conf_init_ptr_value

ngx_conf_init_size_value

ngx_conf_init_uint_value

ngx_conf_init_value

ngx_conf_merge_bitmask_value

ngx_conf_merge_bufs_value

ngx_conf_merge_msec_value

ngx_conf_merge_off_value

ngx_conf_merge_ptr_value

ngx_conf_merge_sec_value

ngx_conf_merge_size_value

ngx_conf_merge_str_value

ngx_conf_merge_uint_value

ngx_conf_merge_value

ngx_get_conf

ngx_null_command

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CONF_FILE_H_INCLUDED_

‌#define _NGX_CONF_FILE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

/*

*        AAAA  number of arguments

*      FF      command flags

*    TT        command type, i.e. HTTP "location" or "server" command

*/

‌#define NGX_CONF_NOARGS      0x00000001

‌#define NGX_CONF_TAKE1       0x00000002

‌#define NGX_CONF_TAKE2       0x00000004

‌#define NGX_CONF_TAKE3       0x00000008

‌#define NGX_CONF_TAKE4       0x00000010

‌#define NGX_CONF_TAKE5       0x00000020

‌#define NGX_CONF_TAKE6       0x00000040

‌#define NGX_CONF_TAKE7       0x00000080

‌#define NGX_CONF_MAX_ARGS    8

‌#define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)

‌#define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)

‌#define NGX_CONF_TAKE23      (NGX_CONF_TAKE2|NGX_CONF_TAKE3)

‌#define NGX_CONF_TAKE123     (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)

‌#define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3   \

                              |NGX_CONF_TAKE4)

‌#define NGX_CONF_ARGS_NUMBER 0x000000ff

‌#define NGX_CONF_BLOCK       0x00000100

‌#define NGX_CONF_FLAG        0x00000200

‌#define NGX_CONF_ANY         0x00000400

‌#define NGX_CONF_1MORE       0x00000800

‌#define NGX_CONF_2MORE       0x00001000

‌#define NGX_CONF_MULTI       0x00000000  /* compatibility */

‌#define NGX_DIRECT_CONF      0x00010000

‌#define NGX_MAIN_CONF        0x01000000

‌#define NGX_ANY_CONF         0x0F000000

‌#define NGX_CONF_UNSET       -1

‌#define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1

‌#define NGX_CONF_UNSET_PTR   (void *) -1

‌#define NGX_CONF_UNSET_SIZE  (size_t) -1

‌#define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1

‌#define NGX_CONF_OK          NULL

‌#define NGX_CONF_ERROR       (void *) -1

‌#define NGX_CONF_BLOCK_START 1

‌#define NGX_CONF_BLOCK_DONE  2

‌#define NGX_CONF_FILE_DONE   3

‌#define NGX_CORE_MODULE      0x45524F43  /* "CORE" */

‌#define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */

‌#define NGX_MAX_CONF_ERRSTR  1024

‌struct ngx_command_s {

    ngx_str_t             name;

    ngx_uint_t            type;

    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

    ngx_uint_t            conf;

    ngx_uint_t            offset;

    void                 *post;

};

‌#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }

‌struct ngx_open_file_s {

    ngx_fd_t              fd;

    ngx_str_t             name;

    void                (*flush)(ngx_open_file_t *file, ngx_log_t *log);

    void                 *data;

};

‌#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1

‌#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

‌struct ngx_module_s {

    ngx_uint_t            ctx_index;

    ngx_uint_t            index;

    ngx_uint_t            spare0;

    ngx_uint_t            spare1;

    ngx_uint_t            spare2;

    ngx_uint_t            spare3;

    ngx_uint_t            version;

    void                 *ctx;

    ngx_command_t        *commands;

    ngx_uint_t            type;

    ngx_int_t           (*init_master)(ngx_log_t *log);

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);

    void                (*exit_thread)(ngx_cycle_t *cycle);

    void                (*exit_process)(ngx_cycle_t *cycle);

    void                (*exit_master)(ngx_cycle_t *cycle);

    uintptr_t             spare_hook0;

    uintptr_t             spare_hook1;

    uintptr_t             spare_hook2;

    uintptr_t             spare_hook3;

    uintptr_t             spare_hook4;

    uintptr_t             spare_hook5;

    uintptr_t             spare_hook6;

    uintptr_t             spare_hook7;

};

typedef struct {

    ngx_str_t             name;

    void               *(*create_conf)(ngx_cycle_t *cycle);

    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);

‌} ngx_core_module_t;

typedef struct {

    ngx_file_t            file;

    ngx_buf_t            *buffer;

    ngx_uint_t            line;

‌} ngx_conf_file_t;

‌typedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf,

    ngx_command_t *dummy, void *conf);

‌struct ngx_conf_s {

    char                 *name;

    ngx_array_t          *args;

    ngx_cycle_t          *cycle;

    ngx_pool_t           *pool;

    ngx_pool_t           *temp_pool;

    ngx_conf_file_t      *conf_file;

    ngx_log_t            *log;

    void                 *ctx;

    ngx_uint_t            module_type;

    ngx_uint_t            cmd_type;

    ngx_conf_handler_pt   handler;

    char                 *handler_conf;

};

‌typedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf,

    void *data, void *conf);

typedef struct {

    ngx_conf_post_handler_pt  post_handler;

‌} ngx_conf_post_t;

typedef struct {

    ngx_conf_post_handler_pt  post_handler;

    char                     *old_name;

    char                     *new_name;

‌} ngx_conf_deprecated_t;

typedef struct {

    ngx_conf_post_handler_pt  post_handler;

    ngx_int_t                 low;

    ngx_int_t                 high;

‌} ngx_conf_num_bounds_t;

typedef struct {

    ngx_str_t                 name;

    ngx_uint_t                value;

‌} ngx_conf_enum_t;

‌#define NGX_CONF_BITMASK_SET  1

typedef struct {

    ngx_str_t                 name;

    ngx_uint_t                mask;

‌} ngx_conf_bitmask_t;

char * ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data);

char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data);

‌#define ngx_get_conf(conf_ctx, module)  conf_ctx[module.index]

‌#define ngx_conf_init_value(conf, default)                                   \

    if (conf == NGX_CONF_UNSET) {                                            \

        conf = default;                                                      \

    }

‌#define ngx_conf_init_ptr_value(conf, default)                               \

    if (conf == NGX_CONF_UNSET_PTR) {                                        \

        conf = default;                                                      \

    }

‌#define ngx_conf_init_uint_value(conf, default)                              \

    if (conf == NGX_CONF_UNSET_UINT) {                                       \

        conf = default;                                                      \

    }

‌#define ngx_conf_init_size_value(conf, default)                              \

    if (conf == NGX_CONF_UNSET_SIZE) {                                       \

        conf = default;                                                      \

    }

‌#define ngx_conf_init_msec_value(conf, default)                              \

    if (conf == NGX_CONF_UNSET_MSEC) {                                       \

        conf = default;                                                      \

    }

‌#define ngx_conf_merge_value(conf, prev, default)                            \

    if (conf == NGX_CONF_UNSET) {                                            \

        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \

    }

‌#define ngx_conf_merge_ptr_value(conf, prev, default)                        \

    if (conf == NGX_CONF_UNSET_PTR) {                                        \

        conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev;                \

    }

‌#define ngx_conf_merge_uint_value(conf, prev, default)                       \

    if (conf == NGX_CONF_UNSET_UINT) {                                       \

        conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev;               \

    }

‌#define ngx_conf_merge_msec_value(conf, prev, default)                       \

    if (conf == NGX_CONF_UNSET_MSEC) {                                       \

        conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev;               \

    }

‌#define ngx_conf_merge_sec_value(conf, prev, default)                        \

    if (conf == NGX_CONF_UNSET) {                                            \

        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \

    }

‌#define ngx_conf_merge_size_value(conf, prev, default)                       \

    if (conf == NGX_CONF_UNSET_SIZE) {                                       \

        conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev;               \

    }

‌#define ngx_conf_merge_off_value(conf, prev, default)                        \

    if (conf == NGX_CONF_UNSET) {                                            \

        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \

    }

‌#define ngx_conf_merge_str_value(conf, prev, default)                        \

    if (conf.data == NULL) {                                                 \

        if (prev.data) {                                                     \

            conf.len = prev.len;                                             \

            conf.data = prev.data;                                           \

        } else {                                                             \

            conf.len = sizeof(default) - 1;                                  \

            conf.data = (u_char *) default;                                  \

        }                                                                    \

    }

‌#define ngx_conf_merge_bufs_value(conf, prev, default_num, default_size)     \

    if (conf.num == 0) {                                                     \

        if (prev.num) {                                                      \

            conf.num = prev.num;                                             \

            conf.size = prev.size;                                           \

        } else {                                                             \

            conf.num = default_num;                                          \

            conf.size = default_size;                                        \

        }                                                                    \

    }

‌#define ngx_conf_merge_bitmask_value(conf, prev, default)                    \

    if (conf == 0) {                                                         \

        conf = (prev == 0) ? default : prev;                                 \

    }

char *ngx_conf_param(ngx_conf_t *cf);

char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);

char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name,

    ngx_uint_t conf_prefix);

ngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name);

void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf,

    ngx_err_t err, const char *fmt, ...);

char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

char *ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

extern ngx_uint_t     ngx_max_module;

extern ngx_module_t  *ngx_modules[];

#endif /* _NGX_CONF_FILE_H_INCLUDED_ */

src/os/unix/ngx_time.h - nginx-1.7.9

Data types defined

ngx_msec_int_t

ngx_msec_t

ngx_tm_t

Macros defined

_NGX_TIME_H_INCLUDED_

ngx_gettimeofday

ngx_msleep

ngx_sleep

ngx_timezone

ngx_timezone

ngx_tm_gmtoff

ngx_tm_hour

ngx_tm_hour_t

ngx_tm_isdst

ngx_tm_mday

ngx_tm_mday_t

ngx_tm_min

ngx_tm_min_t

ngx_tm_mon

ngx_tm_mon_t

ngx_tm_sec

ngx_tm_sec_t

ngx_tm_wday

ngx_tm_wday_t

ngx_tm_year

ngx_tm_year_t

ngx_tm_zone

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_TIME_H_INCLUDED_

‌#define _NGX_TIME_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef ngx_rbtree_key_t      ngx_msec_t;

‌typedef ngx_rbtree_key_int_t  ngx_msec_int_t;

‌typedef struct tm             ngx_tm_t;

‌#define ngx_tm_sec            tm_sec

‌#define ngx_tm_min            tm_min

‌#define ngx_tm_hour           tm_hour

‌#define ngx_tm_mday           tm_mday

‌#define ngx_tm_mon            tm_mon

‌#define ngx_tm_year           tm_year

‌#define ngx_tm_wday           tm_wday

‌#define ngx_tm_isdst          tm_isdst

‌#define ngx_tm_sec_t          int

‌#define ngx_tm_min_t          int

‌#define ngx_tm_hour_t         int

‌#define ngx_tm_mday_t         int

‌#define ngx_tm_mon_t          int

‌#define ngx_tm_year_t         int

‌#define ngx_tm_wday_t         int

#if (NGX_HAVE_GMTOFF)

‌#define ngx_tm_gmtoff         tm_gmtoff

‌#define ngx_tm_zone           tm_zone

#endif

#if (NGX_SOLARIS)

‌#define ngx_timezone(isdst) (- (isdst ? altzone : timezone) / 60)

#else

‌#define ngx_timezone(isdst) (- (isdst ? timezone + 3600 : timezone) / 60)

#endif

void ngx_timezone_update(void);

void ngx_localtime(time_t s, ngx_tm_t *tm);

void ngx_libc_localtime(time_t s, struct tm *tm);

void ngx_libc_gmtime(time_t s, struct tm *tm);

‌#define ngx_gettimeofday(tp)  (void) gettimeofday(tp, NULL);

‌#define ngx_msleep(ms)        (void) usleep(ms * 1000)

‌#define ngx_sleep(s)          (void) sleep(s)

#endif /* _NGX_TIME_H_INCLUDED_ */

src/core/ngx_rbtree.h - nginx-1.7.9

Data types defined

ngx_rbtree_insert_pt

ngx_rbtree_key_int_t

ngx_rbtree_key_t

ngx_rbtree_node_s

ngx_rbtree_node_t

ngx_rbtree_s

ngx_rbtree_t

Functions defined

ngx_rbtree_min

Macros defined

_NGX_RBTREE_H_INCLUDED_

ngx_rbt_black

ngx_rbt_copy_color

ngx_rbt_is_black

ngx_rbt_is_red

ngx_rbt_red

ngx_rbtree_init

ngx_rbtree_sentinel_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_RBTREE_H_INCLUDED_

‌#define _NGX_RBTREE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef ngx_uint_t  ngx_rbtree_key_t;

‌typedef ngx_int_t   ngx_rbtree_key_int_t;

‌typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

‌struct ngx_rbtree_node_s {

    ngx_rbtree_key_t       key;

    ngx_rbtree_node_t     *left;

    ngx_rbtree_node_t     *right;

    ngx_rbtree_node_t     *parent;

    u_char                 color;

    u_char                 data;

};

‌typedef struct ngx_rbtree_s  ngx_rbtree_t;

‌typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

‌struct ngx_rbtree_s {

    ngx_rbtree_node_t     *root;

    ngx_rbtree_node_t     *sentinel;

    ngx_rbtree_insert_pt   insert;

};

‌#define ngx_rbtree_init(tree, s, i)                                           \

    ngx_rbtree_sentinel_init(s);                                              \

    (tree)->root = s;                                                         \

    (tree)->sentinel = s;                                                     \

    (tree)->insert = i

void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);

void ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);

void ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel);

void ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

‌#define ngx_rbt_red(node)               ((node)->color = 1)

‌#define ngx_rbt_black(node)             ((node)->color = 0)

‌#define ngx_rbt_is_red(node)            ((node)->color)

‌#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))

‌#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)

/* a sentinel must be black */

‌#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

static ngx_inline ngx_rbtree_node_t *

‌ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    while (node->left != sentinel) {

        node = node->left;

    }

    return node;

}

#endif /* _NGX_RBTREE_H_INCLUDED_ */

src/core/ngx_rbtree.c - nginx-1.7.9

Functions defined

ngx_rbtree_delete

ngx_rbtree_insert

ngx_rbtree_insert_timer_value

ngx_rbtree_insert_value

ngx_rbtree_left_rotate

ngx_rbtree_right_rotate

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* The red-black tree code is based on the algorithm described in

* the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.

*/

static ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_node_t **root,

    ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);

static ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_node_t **root,

    ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);

void

‌ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)

{

    ngx_rbtree_node_t  **root, *temp, *sentinel;

    /* a binary tree insert */

    root = (ngx_rbtree_node_t **) &tree->root;

    sentinel = tree->sentinel;

    if (*root == sentinel) {

        node->parent = NULL;

        node->left = sentinel;

        node->right = sentinel;

        ngx_rbt_black(node);

        *root = node;

        return;

    }

    tree->insert(*root, node, sentinel);

    /* re-balance tree */

    while (node != *root && ngx_rbt_is_red(node->parent)) {

        if (node->parent == node->parent->parent->left) {

            temp = node->parent->parent->right;

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

            } else {

                if (node == node->parent->right) {

                    node = node->parent;

                    ngx_rbtree_left_rotate(root, sentinel, node);

                }

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);

            }

        } else {

            temp = node->parent->parent->left;

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

            } else {

                if (node == node->parent->left) {

                    node = node->parent;

                    ngx_rbtree_right_rotate(root, sentinel, node);

                }

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);

            }

        }

    }

    ngx_rbt_black(*root);

}

void

‌ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        p = (node->key < temp->key) ? &temp->left : &temp->right;

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

void

‌ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        /*

         * Timer values

         * 1) are spread in small range, usually several minutes,

         * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.

         * The comparison takes into account that overflow.

         */

        /*  node->key < temp->key */

        p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)

            ? &temp->left : &temp->right;

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

void

‌ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)

{

    ngx_uint_t           red;

    ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;

    /* a binary tree delete */

    root = (ngx_rbtree_node_t **) &tree->root;

    sentinel = tree->sentinel;

    if (node->left == sentinel) {

        temp = node->right;

        subst = node;

    } else if (node->right == sentinel) {

        temp = node->left;

        subst = node;

    } else {

        subst = ngx_rbtree_min(node->right, sentinel);

        if (subst->left != sentinel) {

            temp = subst->left;

        } else {

            temp = subst->right;

        }

    }

    if (subst == *root) {

        *root = temp;

        ngx_rbt_black(temp);

        /* DEBUG stuff */

        node->left = NULL;

        node->right = NULL;

        node->parent = NULL;

        node->key = 0;

        return;

    }

    red = ngx_rbt_is_red(subst);

    if (subst == subst->parent->left) {

        subst->parent->left = temp;

    } else {

        subst->parent->right = temp;

    }

    if (subst == node) {

        temp->parent = subst->parent;

    } else {

        if (subst->parent == node) {

            temp->parent = subst;

        } else {

            temp->parent = subst->parent;

        }

        subst->left = node->left;

        subst->right = node->right;

        subst->parent = node->parent;

        ngx_rbt_copy_color(subst, node);

        if (node == *root) {

            *root = subst;

        } else {

            if (node == node->parent->left) {

                node->parent->left = subst;

            } else {

                node->parent->right = subst;

            }

        }

        if (subst->left != sentinel) {

            subst->left->parent = subst;

        }

        if (subst->right != sentinel) {

            subst->right->parent = subst;

        }

    }

    /* DEBUG stuff */

    node->left = NULL;

    node->right = NULL;

    node->parent = NULL;

    node->key = 0;

    if (red) {

        return;

    }

    /* a delete fixup */

    while (temp != *root && ngx_rbt_is_black(temp)) {

        if (temp == temp->parent->left) {

            w = temp->parent->right;

            if (ngx_rbt_is_red(w)) {

                ngx_rbt_black(w);

                ngx_rbt_red(temp->parent);

                ngx_rbtree_left_rotate(root, sentinel, temp->parent);

                w = temp->parent->right;

            }

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {

                ngx_rbt_red(w);

                temp = temp->parent;

            } else {

                if (ngx_rbt_is_black(w->right)) {

                    ngx_rbt_black(w->left);

                    ngx_rbt_red(w);

                    ngx_rbtree_right_rotate(root, sentinel, w);

                    w = temp->parent->right;

                }

                ngx_rbt_copy_color(w, temp->parent);

                ngx_rbt_black(temp->parent);

                ngx_rbt_black(w->right);

                ngx_rbtree_left_rotate(root, sentinel, temp->parent);

                temp = *root;

            }

        } else {

            w = temp->parent->left;

            if (ngx_rbt_is_red(w)) {

                ngx_rbt_black(w);

                ngx_rbt_red(temp->parent);

                ngx_rbtree_right_rotate(root, sentinel, temp->parent);

                w = temp->parent->left;

            }

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {

                ngx_rbt_red(w);

                temp = temp->parent;

            } else {

                if (ngx_rbt_is_black(w->left)) {

                    ngx_rbt_black(w->right);

                    ngx_rbt_red(w);

                    ngx_rbtree_left_rotate(root, sentinel, w);

                    w = temp->parent->left;

                }

                ngx_rbt_copy_color(w, temp->parent);

                ngx_rbt_black(temp->parent);

                ngx_rbt_black(w->left);

                ngx_rbtree_right_rotate(root, sentinel, temp->parent);

                temp = *root;

            }

        }

    }

    ngx_rbt_black(temp);

}

static ngx_inline void

‌ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node)

{

    ngx_rbtree_node_t  *temp;

    temp = node->right;

    node->right = temp->left;

    if (temp->left != sentinel) {

        temp->left->parent = node;

    }

    temp->parent = node->parent;

    if (node == *root) {

        *root = temp;

    } else if (node == node->parent->left) {

        node->parent->left = temp;

    } else {

        node->parent->right = temp;

    }

    temp->left = node;

    node->parent = temp;

}

static ngx_inline void

‌ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node)

{

    ngx_rbtree_node_t  *temp;

    temp = node->left;

    node->left = temp->right;

    if (temp->right != sentinel) {

        temp->right->parent = node;

    }

    temp->parent = node->parent;

    if (node == *root) {

        *root = temp;

    } else if (node == node->parent->right) {

        node->parent->right = temp;

    } else {

        node->parent->left = temp;

    }

    temp->right = node;

    node->parent = temp;

}

src/event/modules/ngx_rtsig_module.c - nginx-1.7.9

Global variables defined

ngx_linux_rtsig_max

ngx_overflow_threshold_bounds

ngx_rtsig_commands

ngx_rtsig_module

ngx_rtsig_module_ctx

overflow

overflow_current

overflow_list

rtsig_name

set

Data types defined

ngx_rtsig_conf_t

Functions defined

ngx_check_ngx_overflow_threshold_bounds

ngx_rtsig_add_connection

ngx_rtsig_create_conf

ngx_rtsig_del_connection

ngx_rtsig_done

ngx_rtsig_init

ngx_rtsig_init_conf

ngx_rtsig_process_events

ngx_rtsig_process_overflow

sigtimedwait

Macros defined

F_SETSIG

KERN_RTSIGMAX

KERN_RTSIGNR

SIGRTMIN

SIGRTMIN

si_fd

si_fd

si_fd

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_TEST_BUILD_RTSIG)

#if (NGX_DARWIN)

‌#define SIGRTMIN       33

‌#define si_fd          __pad[0]

#else

#ifdef  SIGRTMIN

‌#define si_fd          _reason.__spare__.__spare2__[0]

#else

‌#define SIGRTMIN       33

‌#define si_fd          __spare__[0]

#endif

#endif

‌#define F_SETSIG       10

‌#define KERN_RTSIGNR   30

‌#define KERN_RTSIGMAX  31

int sigtimedwait(const sigset_t *set, siginfo_t *info,

                 const struct timespec *timeout);

‌int sigtimedwait(const sigset_t *set, siginfo_t *info,

                 const struct timespec *timeout)

{

    return -1;

}

‌int ngx_linux_rtsig_max;

#endif

typedef struct {

    ngx_uint_t  signo;

    ngx_uint_t  overflow_events;

    ngx_uint_t  overflow_test;

    ngx_uint_t  overflow_threshold;

‌} ngx_rtsig_conf_t;

extern ngx_event_module_t  ngx_poll_module_ctx;

static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_rtsig_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c);

static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c,

    ngx_uint_t flags);

static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle,

    ngx_msec_t timer, ngx_uint_t flags);

static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle,

    ngx_msec_t timer, ngx_uint_t flags);

static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle);

static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf);

static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,

    void *post, void *data);

‌static sigset_t        set;

‌‌static ngx_uint_t      overflow, overflow_current;

‌static struct pollfd  *overflow_list;

‌static ngx_str_t      rtsig_name = ngx_string("rtsig");

‌static ngx_conf_num_bounds_t  ngx_overflow_threshold_bounds = {

    ngx_check_ngx_overflow_threshold_bounds, 2, 10

};

‌static ngx_command_t  ngx_rtsig_commands[] = {

    { ngx_string("rtsig_signo"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_rtsig_conf_t, signo),

      NULL },

    { ngx_string("rtsig_overflow_events"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_rtsig_conf_t, overflow_events),

      NULL },

    { ngx_string("rtsig_overflow_test"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_rtsig_conf_t, overflow_test),

      NULL },

    { ngx_string("rtsig_overflow_threshold"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_rtsig_conf_t, overflow_threshold),

      &ngx_overflow_threshold_bounds },

      ngx_null_command

};

‌ngx_event_module_t  ngx_rtsig_module_ctx = {

    &rtsig_name,

    ngx_rtsig_create_conf,               /* create configuration */

    ngx_rtsig_init_conf,                 /* init configuration */

    {

        NULL,                            /* add an event */

        NULL,                            /* delete an event */

        NULL,                            /* enable an event */

        NULL,                            /* disable an event */

        ngx_rtsig_add_connection,        /* add an connection */

        ngx_rtsig_del_connection,        /* delete an connection */

        NULL,                            /* process the changes */

        ngx_rtsig_process_events,        /* process the events */

        ngx_rtsig_init,                  /* init the events */

        ngx_rtsig_done,                  /* done the events */

    }

};

‌ngx_module_t  ngx_rtsig_module = {

    NGX_MODULE_V1,

    &ngx_rtsig_module_ctx,               /* module context */

    ngx_rtsig_commands,                  /* module directives */

    NGX_EVENT_MODULE,                    /* module type */

    NULL,                                /* init master */

    NULL,                                /* init module */

    NULL,                                /* init process */

    NULL,                                /* init thread */

    NULL,                                /* exit thread */

    NULL,                                /* exit process */

    NULL,                                /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    ngx_rtsig_conf_t  *rtscf;

    rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module);

    sigemptyset(&set);

    sigaddset(&set, (int) rtscf->signo);

    sigaddset(&set, (int) rtscf->signo + 1);

    sigaddset(&set, SIGIO);

    sigaddset(&set, SIGALRM);

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                      "sigprocmask() failed");

        return NGX_ERROR;

    }

    if (overflow_list) {

        ngx_free(overflow_list);

    }

    overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events,

                              cycle->log);

    if (overflow_list == NULL) {

        return NGX_ERROR;

    }

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_rtsig_module_ctx.actions;

    ngx_event_flags = NGX_USE_RTSIG_EVENT

                      |NGX_USE_GREEDY_EVENT

                      |NGX_USE_FD_EVENT;

    return NGX_OK;

}

static void

‌ngx_rtsig_done(ngx_cycle_t *cycle)

{

    ngx_free(overflow_list);

    overflow_list = NULL;

}

static ngx_int_t

‌ngx_rtsig_add_connection(ngx_connection_t *c)

{

    ngx_uint_t         signo;

    ngx_rtsig_conf_t  *rtscf;

    if (c->read->accept && c->read->disabled) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "rtsig enable connection: fd:%d", c->fd);

        if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {

            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                          "fcntl(F_SETOWN) failed");

            return NGX_ERROR;

        }

        c->read->active = 1;

        c->read->disabled = 0;

    }

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    signo = rtscf->signo + c->read->instance;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "rtsig add connection: fd:%d signo:%ui", c->fd, signo);

    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed");

        return NGX_ERROR;

    }

    if (fcntl(c->fd, F_SETSIG, (int) signo) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "fcntl(F_SETSIG) failed");

        return NGX_ERROR;

    }

    if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "fcntl(F_SETOWN) failed");

        return NGX_ERROR;

    }

#if (NGX_HAVE_ONESIGFD)

    if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "fcntl(F_SETAUXFL) failed");

        return NGX_ERROR;

    }

#endif

    c->read->active = 1;

    c->write->active = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_rtsig_del_connection(ngx_connection_t *c, ngx_uint_t flags)

{

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "rtsig del connection: fd:%d", c->fd);

    if ((flags & NGX_DISABLE_EVENT) && c->read->accept) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "rtsig disable connection: fd:%d", c->fd);

        c->read->active = 0;

        c->read->disabled = 1;

        return NGX_OK;

    }

    if (flags & NGX_CLOSE_EVENT) {

        c->read->active = 0;

        c->write->active = 0;

        return NGX_OK;

    }

    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "fcntl(O_RDWR|O_NONBLOCK) failed");

        return NGX_ERROR;

    }

    c->read->active = 0;

    c->write->active = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_rtsig_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)

{

    int                 signo;

    ngx_int_t           instance;

    ngx_err_t           err;

    siginfo_t           si;

    ngx_event_t        *rev, *wev;

    ngx_queue_t        *queue;

    struct timespec     ts, *tp;

    struct sigaction    sa;

    ngx_connection_t   *c;

    ngx_rtsig_conf_t   *rtscf;

    if (timer == NGX_TIMER_INFINITE) {

        tp = NULL;

    } else {

        ts.tv_sec = timer / 1000;

        ts.tv_nsec = (timer % 1000) * 1000000;

        tp = &ts;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "rtsig timer: %M", timer);

    /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */

    signo = sigtimedwait(&set, &si, tp);

    if (signo == -1) {

        err = ngx_errno;

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,

                       "rtsig signo:%d", signo);

        if (flags & NGX_UPDATE_TIME) {

            ngx_time_update();

        }

        if (err == NGX_EAGAIN) {

            /* timeout */

            if (timer != NGX_TIMER_INFINITE) {

                return NGX_AGAIN;

            }

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "sigtimedwait() returned EAGAIN without timeout");

            return NGX_ERROR;

        }

        ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,

                      cycle->log, err, "sigtimedwait() failed");

        return NGX_ERROR;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "rtsig signo:%d fd:%d band:%04Xd",

                   signo, si.si_fd, si.si_band);

    if (flags & NGX_UPDATE_TIME) {

        ngx_time_update();

    }

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    if (signo == (int) rtscf->signo || signo == (int) rtscf->signo + 1) {

        if (overflow && (ngx_uint_t) si.si_fd > overflow_current) {

            return NGX_OK;

        }

        c = ngx_cycle->files[si.si_fd];

        if (c == NULL) {

            /* the stale event */

            return NGX_OK;

        }

        instance = signo - (int) rtscf->signo;

        rev = c->read;

        if (rev->instance != instance) {

            /*

             * the stale event from a file descriptor

             * that was just closed in this iteration

             */

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "rtsig: stale event %p", c);

            return NGX_OK;

        }

        if ((si.si_band & (POLLIN|POLLHUP|POLLERR)) && rev->active) {

            rev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                queue = rev->accept ? &ngx_posted_accept_events

                                    : &ngx_posted_events;

                ngx_post_event(rev, queue);

            } else {

                rev->handler(rev);

            }

        }

        wev = c->write;

        if ((si.si_band & (POLLOUT|POLLHUP|POLLERR)) && wev->active) {

            wev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                ngx_post_event(wev, &ngx_posted_events);

            } else {

                wev->handler(wev);

            }

        }

        return NGX_OK;

    } else if (signo == SIGALRM) {

        ngx_time_update();

        return NGX_OK;

    } else if (signo == SIGIO) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "rt signal queue overflowed");

        /* flush the RT signal queue */

        ngx_memzero(&sa, sizeof(struct sigaction));

        sa.sa_handler = SIG_DFL;

        sigemptyset(&sa.sa_mask);

        if (sigaction(rtscf->signo, &sa, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "sigaction(%d, SIG_DFL) failed", rtscf->signo);

        }

        if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "sigaction(%d, SIG_DFL) failed", rtscf->signo + 1);

        }

        overflow = 1;

        overflow_current = 0;

        ngx_event_actions.process_events = ngx_rtsig_process_overflow;

        return NGX_ERROR;

    }

    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                  "sigtimedwait() returned unexpected signal: %d", signo);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_rtsig_process_overflow(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int                name[2], rtsig_max, rtsig_nr, events, ready;

    size_t             len;

    ngx_err_t          err;

    ngx_uint_t         tested, n, i;

    ngx_event_t       *rev, *wev;

    ngx_queue_t       *queue;

    ngx_connection_t  *c;

    ngx_rtsig_conf_t  *rtscf;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "rtsig process overflow");

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    tested = 0;

    for ( ;; ) {

        n = 0;

        while (n < rtscf->overflow_events) {

            if (overflow_current == cycle->connection_n) {

                break;

            }

            c = cycle->files[overflow_current++];

            if (c == NULL || c->fd == -1) {

                continue;

            }

            events = 0;

            if (c->read->active && c->read->handler) {

                events |= POLLIN;

            }

            if (c->write->active && c->write->handler) {

                events |= POLLOUT;

            }

            if (events == 0) {

                continue;

            }

            overflow_list[n].fd = c->fd;

            overflow_list[n].events = events;

            overflow_list[n].revents = 0;

            n++;

        }

        if (n == 0) {

            break;

        }

        for ( ;; ) {

            ready = poll(overflow_list, n, 0);

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "rtsig overflow poll:%d", ready);

            if (ready == -1) {

                err = ngx_errno;

                ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,

                              cycle->log, 0,

                              "poll() failed while the overflow recover");

                if (err == NGX_EINTR) {

                    continue;

                }

            }

            break;

        }

        if (ready <= 0) {

            continue;

        }

        for (i = 0; i < n; i++) {

            c = cycle->files[overflow_list[i].fd];

            if (c == NULL) {

                continue;

            }

            rev = c->read;

            if (rev->active

                && !rev->closed

                && rev->handler

                && (overflow_list[i].revents

                                          & (POLLIN|POLLERR|POLLHUP|POLLNVAL)))

            {

                tested++;

                rev->ready = 1;

                if (flags & NGX_POST_EVENTS) {

                    queue = rev->accept ? &ngx_posted_accept_events

                                        : &ngx_posted_events;

                    ngx_post_event(rev, queue);

                } else {

                    rev->handler(rev);

                }

            }

            wev = c->write;

            if (wev->active

                && !wev->closed

                && wev->handler

                && (overflow_list[i].revents

                                         & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)))

            {

                tested++;

                wev->ready = 1;

                if (flags & NGX_POST_EVENTS) {

                    ngx_post_event(wev, &ngx_posted_events);

                } else {

                    wev->handler(wev);

                }

            }

        }

        if (tested >= rtscf->overflow_test) {

            if (ngx_linux_rtsig_max) {

                /*

                 * Check the current rt queue length to prevent

                 * the new overflow.

                 *

                 * learn the "/proc/sys/kernel/rtsig-max" value because

                 * it can be changed since the last checking

                 */

                name[0] = CTL_KERN;

                name[1] = KERN_RTSIGMAX;

                len = sizeof(rtsig_max);

                if (sysctl(name, 2, &rtsig_max, &len, NULL, 0) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,

                                  "sysctl(KERN_RTSIGMAX) failed");

                    return NGX_ERROR;

                }

                /* name[0] = CTL_KERN; */

                name[1] = KERN_RTSIGNR;

                len = sizeof(rtsig_nr);

                if (sysctl(name, 2, &rtsig_nr, &len, NULL, 0) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,

                                  "sysctl(KERN_RTSIGNR) failed");

                    return NGX_ERROR;

                }

                /*

                 * drain the rt signal queue if the /"proc/sys/kernel/rtsig-nr"

                 * is bigger than

                 *    "/proc/sys/kernel/rtsig-max" / "rtsig_overflow_threshold"

                 */

                if (rtsig_max / (int) rtscf->overflow_threshold < rtsig_nr) {

                    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                                   "rtsig queue state: %d/%d",

                                   rtsig_nr, rtsig_max);

                    while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK)

                    {

                        /* void */

                    }

                }

            } else {

                /*

                 * Linux has not KERN_RTSIGMAX since 2.6.6-mm2

                 * so drain the rt signal queue unconditionally

                 */

                while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK) {

                    /* void */

                }

            }

            tested = 0;

        }

    }

    if (flags & NGX_UPDATE_TIME) {

        ngx_time_update();

    }

    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                  "rt signal queue overflow recovered");

    overflow = 0;

    ngx_event_actions.process_events = ngx_rtsig_process_events;

    return NGX_OK;

}

static void *

‌ngx_rtsig_create_conf(ngx_cycle_t *cycle)

{

    ngx_rtsig_conf_t  *rtscf;

    rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t));

    if (rtscf == NULL) {

        return NULL;

    }

    rtscf->signo = NGX_CONF_UNSET;

    rtscf->overflow_events = NGX_CONF_UNSET;

    rtscf->overflow_test = NGX_CONF_UNSET;

    rtscf->overflow_threshold = NGX_CONF_UNSET;

    return rtscf;

}

static char *

‌ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_rtsig_conf_t  *rtscf = conf;

    /* LinuxThreads use the first 3 RT signals */

    ngx_conf_init_uint_value(rtscf->signo, SIGRTMIN + 10);

    ngx_conf_init_uint_value(rtscf->overflow_events, 16);

    ngx_conf_init_uint_value(rtscf->overflow_test, 32);

    ngx_conf_init_uint_value(rtscf->overflow_threshold, 10);

    return NGX_CONF_OK;

}

static char *

‌ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, void *post, void *data)

{

    if (ngx_linux_rtsig_max) {

        return ngx_conf_check_num_bounds(cf, post, data);

    }

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"rtsig_overflow_threshold\" is not supported "

                       "since Linux 2.6.6-mm2, ignored");

    return NGX_CONF_OK;

}

src/event/ngx_event.h - nginx-1.7.9

Data types defined

ngx_event_actions_t

ngx_event_aio_s

ngx_event_conf_t

ngx_event_module_t

ngx_event_mutex_t

ngx_event_ovlp_t

ngx_event_s

Macros defined

EPOLLRDHUP

NGX_CLEAR_EVENT

NGX_CLEAR_EVENT

NGX_CLEAR_EVENT

NGX_CLOSE_EVENT

NGX_CLOSE_EVENT

NGX_CLOSE_EVENT

NGX_DISABLE_EVENT

NGX_DISABLE_EVENT

NGX_DISABLE_EVENT

NGX_EVENT_CONF

NGX_EVENT_MODULE

NGX_FLUSH_EVENT

NGX_FLUSH_EVENT

NGX_FLUSH_EVENT

NGX_INVALID_INDEX

NGX_IOCP_ACCEPT

NGX_IOCP_CONNECT

NGX_IOCP_IO

NGX_LEVEL_EVENT

NGX_LEVEL_EVENT

NGX_LEVEL_EVENT

NGX_LEVEL_EVENT

NGX_LEVEL_EVENT

NGX_LOWAT_EVENT

NGX_LOWAT_EVENT

NGX_LOWAT_EVENT

NGX_ONESHOT_EVENT

NGX_ONESHOT_EVENT

NGX_ONESHOT_EVENT

NGX_ONESHOT_EVENT

NGX_ONESHOT_EVENT

NGX_POST_EVENTS

NGX_READ_EVENT

NGX_READ_EVENT

NGX_READ_EVENT

NGX_READ_EVENT

NGX_READ_EVENT

NGX_UPDATE_TIME

NGX_USE_AIO_EVENT

NGX_USE_CLEAR_EVENT

NGX_USE_EPOLL_EVENT

NGX_USE_EVENTPORT_EVENT

NGX_USE_FD_EVENT

NGX_USE_GREEDY_EVENT

NGX_USE_IOCP_EVENT

NGX_USE_KQUEUE_EVENT

NGX_USE_LEVEL_EVENT

NGX_USE_LOWAT_EVENT

NGX_USE_ONESHOT_EVENT

NGX_USE_RTSIG_EVENT

NGX_USE_TIMER_EVENT

NGX_USE_VNODE_EVENT

NGX_VNODE_EVENT

NGX_VNODE_EVENT

NGX_VNODE_EVENT

NGX_WRITE_EVENT

NGX_WRITE_EVENT

NGX_WRITE_EVENT

NGX_WRITE_EVENT

NGX_WRITE_EVENT

_NGX_EVENT_H_INCLUDED_

ngx_add_conn

ngx_add_event

ngx_add_timer

ngx_del_conn

ngx_del_event

ngx_del_timer

ngx_done_events

ngx_event_get_conf

ngx_event_ident

ngx_process_changes

ngx_process_events

ngx_recv

ngx_recv_chain

ngx_send

ngx_send_chain

ngx_udp_recv

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_H_INCLUDED_

‌#define _NGX_EVENT_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_INVALID_INDEX  0xd0d0d0d0

#if (NGX_HAVE_IOCP)

typedef struct {

    WSAOVERLAPPED    ovlp;

    ngx_event_t     *event;

    int              error;

‌} ngx_event_ovlp_t;

#endif

typedef struct {

    ngx_uint_t       lock;

    ngx_event_t     *events;

    ngx_event_t     *last;

‌} ngx_event_mutex_t;

‌struct ngx_event_s {

    void            *data;

    unsigned         write:1;

    unsigned         accept:1;

    /* used to detect the stale events in kqueue, rtsig, and epoll */

    unsigned         instance:1;

    /*

     * the event was passed or would be passed to a kernel;

     * in aio mode - operation was posted.

     */

    unsigned         active:1;

    unsigned         disabled:1;

    /* the ready event; in aio mode 0 means that no operation can be posted */

    unsigned         ready:1;

    unsigned         oneshot:1;

    /* aio operation is complete */

    unsigned         complete:1;

    unsigned         eof:1;

    unsigned         error:1;

    unsigned         timedout:1;

    unsigned         timer_set:1;

    unsigned         delayed:1;

    unsigned         deferred_accept:1;

    /* the pending eof reported by kqueue, epoll or in aio chain operation */

    unsigned         pending_eof:1;

    unsigned         posted:1;

#if (NGX_WIN32)

    /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */

    unsigned         accept_context_updated:1;

#endif

#if (NGX_HAVE_KQUEUE)

    unsigned         kq_vnode:1;

    /* the pending errno reported by kqueue */

    int              kq_errno;

#endif

    /*

     * kqueue only:

     *   accept:     number of sockets that wait to be accepted

     *   read:       bytes to read when event is ready

     *               or lowat when event is set with NGX_LOWAT_EVENT flag

     *   write:      available space in buffer when event is ready

     *               or lowat when event is set with NGX_LOWAT_EVENT flag

     *

     * iocp: TODO

     *

     * otherwise:

     *   accept:     1 if accept many, 0 otherwise

     */

#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)

    int              available;

#else

    unsigned         available:1;

#endif

    ngx_event_handler_pt  handler;

#if (NGX_HAVE_AIO)

#if (NGX_HAVE_IOCP)

    ngx_event_ovlp_t ovlp;

#else

    struct aiocb     aiocb;

#endif

#endif

    ngx_uint_t       index;

    ngx_log_t       *log;

    ngx_rbtree_node_t   timer;

    /* the posted queue */

    ngx_queue_t      queue;

    unsigned         closed:1;

    /* to test on worker exit */

    unsigned         channel:1;

    unsigned         resolver:1;

    unsigned         cancelable:1;

#if 0

    /* the threads support */

    /*

     * the event thread context, we store it here

     * if $(CC) does not understand __thread declaration

     * and pthread_getspecific() is too costly

     */

    void            *thr_ctx;

#if (NGX_EVENT_T_PADDING)

    /* event should not cross cache line in SMP */

    uint32_t         padding[NGX_EVENT_T_PADDING];

#endif

#endif

};

#if (NGX_HAVE_FILE_AIO)

‌struct ngx_event_aio_s {

    void                      *data;

    ngx_event_handler_pt       handler;

    ngx_file_t                *file;

    ngx_fd_t                   fd;

#if (NGX_HAVE_EVENTFD)

    int64_t                    res;

#if (NGX_TEST_BUILD_EPOLL)

    ngx_err_t                  err;

    size_t                     nbytes;

#endif

#else

    ngx_err_t                  err;

    size_t                     nbytes;

#endif

#if (NGX_HAVE_AIO_SENDFILE)

    off_t                      last_offset;

#endif

    ngx_aiocb_t                aiocb;

    ngx_event_t                event;

};

#endif

typedef struct {

    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*add_conn)(ngx_connection_t *c);

    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);

    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,

                   ngx_uint_t flags);

    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);

    void       (*done)(ngx_cycle_t *cycle);

‌} ngx_event_actions_t;

extern ngx_event_actions_t   ngx_event_actions;

/*

* The event filter requires to read/write the whole data:

* select, poll, /dev/poll, kqueue, epoll.

*/

‌#define NGX_USE_LEVEL_EVENT      0x00000001

/*

* The event filter is deleted after a notification without an additional

* syscall: kqueue, epoll.

*/

‌#define NGX_USE_ONESHOT_EVENT    0x00000002

/*

* The event filter notifies only the changes and an initial level:

* kqueue, epoll.

*/

‌#define NGX_USE_CLEAR_EVENT      0x00000004

/*

* The event filter has kqueue features: the eof flag, errno,

* available data, etc.

*/

‌#define NGX_USE_KQUEUE_EVENT     0x00000008

/*

* The event filter supports low water mark: kqueue's NOTE_LOWAT.

* kqueue in FreeBSD 4.1-4.2 has no NOTE_LOWAT so we need a separate flag.

*/

‌#define NGX_USE_LOWAT_EVENT      0x00000010

/*

* The event filter requires to do i/o operation until EAGAIN: epoll, rtsig.

*/

‌#define NGX_USE_GREEDY_EVENT     0x00000020

/*

* The event filter is epoll.

*/

‌#define NGX_USE_EPOLL_EVENT      0x00000040

/*

* No need to add or delete the event filters: rtsig.

*/

‌#define NGX_USE_RTSIG_EVENT      0x00000080

/*

* No need to add or delete the event filters: overlapped, aio_read,

* aioread, io_submit.

*/

‌#define NGX_USE_AIO_EVENT        0x00000100

/*

* Need to add socket or handle only once: i/o completion port.

* It also requires NGX_HAVE_AIO and NGX_USE_AIO_EVENT to be set.

*/

‌#define NGX_USE_IOCP_EVENT       0x00000200

/*

* The event filter has no opaque data and requires file descriptors table:

* poll, /dev/poll, rtsig.

*/

‌#define NGX_USE_FD_EVENT         0x00000400

/*

* The event module handles periodic or absolute timer event by itself:

* kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.

*/

‌#define NGX_USE_TIMER_EVENT      0x00000800

/*

* All event filters on file descriptor are deleted after a notification:

* Solaris 10's event ports.

*/

‌#define NGX_USE_EVENTPORT_EVENT  0x00001000

/*

* The event filter support vnode notifications: kqueue.

*/

‌#define NGX_USE_VNODE_EVENT      0x00002000

/*

* The event filter is deleted just before the closing file.

* Has no meaning for select and poll.

* kqueue, epoll, rtsig, eventport:  allows to avoid explicit delete,

*                                   because filter automatically is deleted

*                                   on file close,

*

* /dev/poll:                        we need to flush POLLREMOVE event

*                                   before closing file.

*/

‌#define NGX_CLOSE_EVENT    1

/*

* disable temporarily event filter, this may avoid locks

* in kernel malloc()/free(): kqueue.

*/

‌#define NGX_DISABLE_EVENT  2

/*

* event must be passed to kernel right now, do not wait until batch processing.

*/

‌#define NGX_FLUSH_EVENT    4

/* these flags have a meaning only for kqueue */

‌#define NGX_LOWAT_EVENT    0

‌#define NGX_VNODE_EVENT    0

#if (NGX_HAVE_EPOLL) && !(NGX_HAVE_EPOLLRDHUP)

‌#define EPOLLRDHUP         0

#endif

#if (NGX_HAVE_KQUEUE)

‌#define NGX_READ_EVENT     EVFILT_READ

‌#define NGX_WRITE_EVENT    EVFILT_WRITE

‌#undef  NGX_VNODE_EVENT

‌#define NGX_VNODE_EVENT    EVFILT_VNODE

/*

* NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags

* and they must not go into a kernel so we need to choose the value

* that must not interfere with any existent and future kqueue flags.

* kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR:

* they are reserved and cleared on a kernel entrance.

*/

‌#undef  NGX_CLOSE_EVENT

‌#define NGX_CLOSE_EVENT    EV_EOF

‌#undef  NGX_LOWAT_EVENT

‌#define NGX_LOWAT_EVENT    EV_FLAG1

‌#undef  NGX_FLUSH_EVENT

‌#define NGX_FLUSH_EVENT    EV_ERROR

‌#define NGX_LEVEL_EVENT    0

‌#define NGX_ONESHOT_EVENT  EV_ONESHOT

‌#define NGX_CLEAR_EVENT    EV_CLEAR

‌#undef  NGX_DISABLE_EVENT

‌#define NGX_DISABLE_EVENT  EV_DISABLE

#elif (NGX_HAVE_DEVPOLL || NGX_HAVE_EVENTPORT)

‌#define NGX_READ_EVENT     POLLIN

‌#define NGX_WRITE_EVENT    POLLOUT

‌#define NGX_LEVEL_EVENT    0

‌#define NGX_ONESHOT_EVENT  1

#elif (NGX_HAVE_EPOLL)

‌#define NGX_READ_EVENT     (EPOLLIN|EPOLLRDHUP)

‌#define NGX_WRITE_EVENT    EPOLLOUT

‌#define NGX_LEVEL_EVENT    0

‌#define NGX_CLEAR_EVENT    EPOLLET

‌#define NGX_ONESHOT_EVENT  0x70000000

#if 0

#define NGX_ONESHOT_EVENT  EPOLLONESHOT

#endif

#elif (NGX_HAVE_POLL)

‌#define NGX_READ_EVENT     POLLIN

‌#define NGX_WRITE_EVENT    POLLOUT

‌#define NGX_LEVEL_EVENT    0

‌#define NGX_ONESHOT_EVENT  1

#else /* select */

‌#define NGX_READ_EVENT     0

‌#define NGX_WRITE_EVENT    1

‌#define NGX_LEVEL_EVENT    0

‌#define NGX_ONESHOT_EVENT  1

#endif /* NGX_HAVE_KQUEUE */

#if (NGX_HAVE_IOCP)

‌#define NGX_IOCP_ACCEPT      0

‌#define NGX_IOCP_IO          1

‌#define NGX_IOCP_CONNECT     2

#endif

#ifndef NGX_CLEAR_EVENT

‌#define NGX_CLEAR_EVENT    0    /* dummy declaration */

#endif

‌#define ngx_process_changes  ngx_event_actions.process_changes

‌#define ngx_process_events   ngx_event_actions.process_events

‌#define ngx_done_events      ngx_event_actions.done

‌#define ngx_add_event        ngx_event_actions.add

‌#define ngx_del_event        ngx_event_actions.del

‌#define ngx_add_conn         ngx_event_actions.add_conn

‌#define ngx_del_conn         ngx_event_actions.del_conn

‌#define ngx_add_timer        ngx_event_add_timer

‌#define ngx_del_timer        ngx_event_del_timer

extern ngx_os_io_t  ngx_io;

‌#define ngx_recv             ngx_io.recv

‌#define ngx_recv_chain       ngx_io.recv_chain

‌#define ngx_udp_recv         ngx_io.udp_recv

‌#define ngx_send             ngx_io.send

‌#define ngx_send_chain       ngx_io.send_chain

‌#define NGX_EVENT_MODULE      0x544E5645  /* "EVNT" */

‌#define NGX_EVENT_CONF        0x02000000

typedef struct {

    ngx_uint_t    connections;

    ngx_uint_t    use;

    ngx_flag_t    multi_accept;

    ngx_flag_t    accept_mutex;

    ngx_msec_t    accept_mutex_delay;

    u_char       *name;

#if (NGX_DEBUG)

    ngx_array_t   debug_connection;

#endif

‌} ngx_event_conf_t;

typedef struct {

    ngx_str_t              *name;

    void                 *(*create_conf)(ngx_cycle_t *cycle);

    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);

    ngx_event_actions_t     actions;

‌} ngx_event_module_t;

extern ngx_atomic_t          *ngx_connection_counter;

extern ngx_atomic_t          *ngx_accept_mutex_ptr;

extern ngx_shmtx_t            ngx_accept_mutex;

extern ngx_uint_t             ngx_use_accept_mutex;

extern ngx_uint_t             ngx_accept_events;

extern ngx_uint_t             ngx_accept_mutex_held;

extern ngx_msec_t             ngx_accept_mutex_delay;

extern ngx_int_t              ngx_accept_disabled;

#if (NGX_STAT_STUB)

extern ngx_atomic_t  *ngx_stat_accepted;

extern ngx_atomic_t  *ngx_stat_handled;

extern ngx_atomic_t  *ngx_stat_requests;

extern ngx_atomic_t  *ngx_stat_active;

extern ngx_atomic_t  *ngx_stat_reading;

extern ngx_atomic_t  *ngx_stat_writing;

extern ngx_atomic_t  *ngx_stat_waiting;

#endif

‌#define NGX_UPDATE_TIME         1

‌#define NGX_POST_EVENTS         2

extern sig_atomic_t           ngx_event_timer_alarm;

extern ngx_uint_t             ngx_event_flags;

extern ngx_module_t           ngx_events_module;

extern ngx_module_t           ngx_event_core_module;

‌#define ngx_event_get_conf(conf_ctx, module)                                  \

             (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];

void ngx_event_accept(ngx_event_t *ev);

ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle);

u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len);

void ngx_process_events_and_timers(ngx_cycle_t *cycle);

ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);

ngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat);

#if (NGX_WIN32)

void ngx_event_acceptex(ngx_event_t *ev);

ngx_int_t ngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n);

u_char *ngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len);

#endif

ngx_int_t ngx_send_lowat(ngx_connection_t *c, size_t lowat);

/* used in ngx_log_debugX() */

‌#define ngx_event_ident(p)  ((ngx_connection_t *) (p))->fd

#include <ngx_event_timer.h>

#include <ngx_event_posted.h>

#include <ngx_event_busy_lock.h>

#if (NGX_WIN32)

#include <ngx_iocp_module.h>

#endif

#endif /* _NGX_EVENT_H_INCLUDED_ */

src/core/ngx_queue.h - nginx-1.7.9

Data types defined

ngx_queue_s

ngx_queue_t

Macros defined

_NGX_QUEUE_H_INCLUDED_

ngx_queue_add

ngx_queue_data

ngx_queue_empty

ngx_queue_head

ngx_queue_init

ngx_queue_insert_after

ngx_queue_insert_head

ngx_queue_insert_tail

ngx_queue_last

ngx_queue_next

ngx_queue_prev

ngx_queue_remove

ngx_queue_remove

ngx_queue_sentinel

ngx_queue_split

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#ifndef _NGX_QUEUE_H_INCLUDED_

‌#define _NGX_QUEUE_H_INCLUDED_

‌typedef struct ngx_queue_s  ngx_queue_t;

‌struct ngx_queue_s {

    ngx_queue_t  *prev;

    ngx_queue_t  *next;

};

‌#define ngx_queue_init(q)                                                     \

    (q)->prev = q;                                                            \

    (q)->next = q

‌#define ngx_queue_empty(h)                                                    \

    (h == (h)->prev)

‌#define ngx_queue_insert_head(h, x)                                           \

    (x)->next = (h)->next;                                                    \

    (x)->next->prev = x;                                                      \

    (x)->prev = h;                                                            \

    (h)->next = x

‌#define ngx_queue_insert_after   ngx_queue_insert_head

‌#define ngx_queue_insert_tail(h, x)                                           \

    (x)->prev = (h)->prev;                                                    \

    (x)->prev->next = x;                                                      \

    (x)->next = h;                                                            \

    (h)->prev = x

‌#define ngx_queue_head(h)                                                     \

    (h)->next

‌#define ngx_queue_last(h)                                                     \

    (h)->prev

‌#define ngx_queue_sentinel(h)                                                 \

    (h)

‌#define ngx_queue_next(q)                                                     \

    (q)->next

‌#define ngx_queue_prev(q)                                                     \

    (q)->prev

#if (NGX_DEBUG)

‌#define ngx_queue_remove(x)                                                   \

    (x)->next->prev = (x)->prev;                                              \

    (x)->prev->next = (x)->next;                                              \

    (x)->prev = NULL;                                                         \

    (x)->next = NULL

#else

‌#define ngx_queue_remove(x)                                                   \

    (x)->next->prev = (x)->prev;                                              \

    (x)->prev->next = (x)->next

#endif

‌#define ngx_queue_split(h, q, n)                                              \

    (n)->prev = (h)->prev;                                                    \

    (n)->prev->next = n;                                                      \

    (n)->next = q;                                                            \

    (h)->prev = (q)->prev;                                                    \

    (h)->prev->next = h;                                                      \

    (q)->prev = n;

‌#define ngx_queue_add(h, n)                                                   \

    (h)->prev->next = (n)->next;                                              \

    (n)->next->prev = (h)->prev;                                              \

    (h)->prev = (n)->prev;                                                    \

    (h)->prev->next = h;

‌#define ngx_queue_data(q, type, link)                                         \

    (type *) ((u_char *) q - offsetof(type, link))

ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue);

void ngx_queue_sort(ngx_queue_t *queue,

    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));

#endif /* _NGX_QUEUE_H_INCLUDED_ */

src/core/ngx_queue.c - nginx-1.7.9

Functions defined

ngx_queue_middle

ngx_queue_sort

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* find the middle queue element if the queue has odd number of elements

* or the first element of the queue's second part otherwise

*/

ngx_queue_t *

‌ngx_queue_middle(ngx_queue_t *queue)

{

    ngx_queue_t  *middle, *next;

    middle = ngx_queue_head(queue);

    if (middle == ngx_queue_last(queue)) {

        return middle;

    }

    next = ngx_queue_head(queue);

    for ( ;; ) {

        middle = ngx_queue_next(middle);

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {

            return middle;

        }

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {

            return middle;

        }

    }

}

/* the stable insertion sort */

void

‌ngx_queue_sort(ngx_queue_t *queue,

    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))

{

    ngx_queue_t  *q, *prev, *next;

    q = ngx_queue_head(queue);

    if (q == ngx_queue_last(queue)) {

        return;

    }

    for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {

        prev = ngx_queue_prev(q);

        next = ngx_queue_next(q);

        ngx_queue_remove(q);

        do {

            if (cmp(prev, q) <= 0) {

                break;

            }

            prev = ngx_queue_prev(prev);

        } while (prev != ngx_queue_sentinel(queue));

        ngx_queue_insert_after(prev, q);

    }

}

src/os/unix/ngx_files.h - nginx-1.7.9

Data types defined

ngx_dir_t

ngx_fd_t

ngx_file_info_t

ngx_file_mapping_t

ngx_file_uniq_t

ngx_glob_t

Functions defined

ngx_de_info

ngx_write_fd

Macros defined

NGX_AT_FDCWD

NGX_DIR_MASK_LEN

NGX_FILE_APPEND

NGX_FILE_CREATE_OR_OPEN

NGX_FILE_DEFAULT_ACCESS

NGX_FILE_DIRECTORY

NGX_FILE_DIRECTORY

NGX_FILE_ERROR

NGX_FILE_NOFOLLOW

NGX_FILE_NONBLOCK

NGX_FILE_OPEN

NGX_FILE_OWNER_ACCESS

NGX_FILE_RDONLY

NGX_FILE_RDWR

NGX_FILE_SEARCH

NGX_FILE_SEARCH

NGX_FILE_SEARCH

NGX_FILE_SEARCH

NGX_FILE_TRUNCATE

NGX_FILE_WRONLY

NGX_HAVE_CASELESS_FILESYSTEM

NGX_HAVE_MAX_PATH

NGX_HAVE_READ_AHEAD

NGX_HAVE_READ_AHEAD

NGX_INVALID_FILE

NGX_LINEFEED

NGX_LINEFEED_SIZE

NGX_MAX_PATH

NGX_MAX_PATH

_NGX_FILES_H_INCLUDED_

ngx_change_file_access

ngx_change_file_access_n

ngx_close_dir

ngx_close_dir_n

ngx_close_file

ngx_close_file_n

ngx_create_dir

ngx_create_dir_n

ngx_de_access

ngx_de_fs_size

ngx_de_info_n

ngx_de_is_dir

ngx_de_is_dir

ngx_de_is_file

ngx_de_is_file

ngx_de_is_link

ngx_de_is_link

ngx_de_link_info

ngx_de_link_info_n

ngx_de_mtime

ngx_de_name

ngx_de_namelen

ngx_de_namelen

ngx_de_size

ngx_delete_dir

ngx_delete_dir_n

ngx_delete_file

ngx_delete_file_n

ngx_dir_access

ngx_directio_off_n

ngx_directio_on

ngx_directio_on

ngx_directio_on

ngx_directio_on_n

ngx_directio_on_n

ngx_directio_on_n

ngx_directio_on_n

ngx_fd_info

ngx_fd_info_n

ngx_file_access

ngx_file_at_info

ngx_file_at_info_n

ngx_file_fs_size

ngx_file_info

ngx_file_info_n

ngx_file_mtime

ngx_file_size

ngx_file_uniq

ngx_getcwd

ngx_getcwd_n

ngx_is_dir

ngx_is_exec

ngx_is_file

ngx_is_link

ngx_linefeed

ngx_link_info

ngx_link_info_n

ngx_lock_fd_n

ngx_open_dir_n

ngx_open_file

ngx_open_file

ngx_open_file_n

ngx_open_glob_n

ngx_open_tempfile_n

ngx_openat_file

ngx_openat_file_n

ngx_path_separator

ngx_read_ahead

ngx_read_ahead

ngx_read_ahead_n

ngx_read_ahead_n

ngx_read_ahead_n

ngx_read_dir_n

ngx_read_fd

ngx_read_fd_n

ngx_read_file_n

ngx_read_file_n

ngx_realpath

ngx_realpath_n

ngx_rename_file

ngx_rename_file_n

ngx_set_file_time_n

ngx_set_stderr

ngx_set_stderr_n

ngx_stderr

ngx_trylock_fd_n

ngx_unlock_fd_n

ngx_write_console

ngx_write_fd_n

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_FILES_H_INCLUDED_

‌#define _NGX_FILES_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef int                      ngx_fd_t;

‌typedef struct stat              ngx_file_info_t;

‌typedef ino_t                    ngx_file_uniq_t;

typedef struct {

    u_char                      *name;

    size_t                       size;

    void                        *addr;

    ngx_fd_t                     fd;

    ngx_log_t                   *log;

‌} ngx_file_mapping_t;

typedef struct {

    DIR                         *dir;

    struct dirent               *de;

    struct stat                  info;

    unsigned                     type:8;

    unsigned                     valid_info:1;

‌} ngx_dir_t;

typedef struct {

    size_t                       n;

    glob_t                       pglob;

    u_char                      *pattern;

    ngx_log_t                   *log;

    ngx_uint_t                   test;

‌} ngx_glob_t;

‌#define NGX_INVALID_FILE         -1

‌#define NGX_FILE_ERROR           -1

#ifdef __CYGWIN__

#ifndef NGX_HAVE_CASELESS_FILESYSTEM

‌#define NGX_HAVE_CASELESS_FILESYSTEM  1

#endif

‌#define ngx_open_file(name, mode, create, access)                            \

    open((const char *) name, mode|create|O_BINARY, access)

#else

‌#define ngx_open_file(name, mode, create, access)                            \

    open((const char *) name, mode|create, access)

#endif

‌#define ngx_open_file_n          "open()"

‌#define NGX_FILE_RDONLY          O_RDONLY

‌#define NGX_FILE_WRONLY          O_WRONLY

‌#define NGX_FILE_RDWR            O_RDWR

‌#define NGX_FILE_CREATE_OR_OPEN  O_CREAT

‌#define NGX_FILE_OPEN            0

‌#define NGX_FILE_TRUNCATE        (O_CREAT|O_TRUNC)

‌#define NGX_FILE_APPEND          (O_WRONLY|O_APPEND)

‌#define NGX_FILE_NONBLOCK        O_NONBLOCK

#if (NGX_HAVE_OPENAT)

‌#define NGX_FILE_NOFOLLOW        O_NOFOLLOW

#if defined(O_DIRECTORY)

‌#define NGX_FILE_DIRECTORY       O_DIRECTORY

#else

‌#define NGX_FILE_DIRECTORY       0

#endif

#if defined(O_SEARCH)

‌#define NGX_FILE_SEARCH          (O_SEARCH|NGX_FILE_DIRECTORY)

#elif defined(O_EXEC)

‌#define NGX_FILE_SEARCH          (O_EXEC|NGX_FILE_DIRECTORY)

#elif (NGX_HAVE_O_PATH)

‌#define NGX_FILE_SEARCH          (O_PATH|O_RDONLY|NGX_FILE_DIRECTORY)

#else

‌#define NGX_FILE_SEARCH          (O_RDONLY|NGX_FILE_DIRECTORY)

#endif

#endif /* NGX_HAVE_OPENAT */

‌#define NGX_FILE_DEFAULT_ACCESS  0644

‌#define NGX_FILE_OWNER_ACCESS    0600

‌#define ngx_close_file           close

‌#define ngx_close_file_n         "close()"

‌#define ngx_delete_file(name)    unlink((const char *) name)

‌#define ngx_delete_file_n        "unlink()"

ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent,

    ngx_uint_t access);

‌#define ngx_open_tempfile_n      "open()"

ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset);

#if (NGX_HAVE_PREAD)

‌#define ngx_read_file_n          "pread()"

#else

‌#define ngx_read_file_n          "read()"

#endif

ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size,

    off_t offset);

ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,

    off_t offset, ngx_pool_t *pool);

‌#define ngx_read_fd              read

‌#define ngx_read_fd_n            "read()"

/*

* we use inlined function instead of simple #define

* because glibc 2.3 sets warn_unused_result attribute for write()

* and in this case gcc 4.3 ignores (void) cast

*/

static ngx_inline ssize_t

‌ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)

{

    return write(fd, buf, n);

}

‌#define ngx_write_fd_n           "write()"

‌#define ngx_write_console        ngx_write_fd

‌#define ngx_linefeed(p)          *p++ = LF;

‌#define NGX_LINEFEED_SIZE        1

‌#define NGX_LINEFEED             "\x0a"

‌#define ngx_rename_file(o, n)    rename((const char *) o, (const char *) n)

‌#define ngx_rename_file_n        "rename()"

‌#define ngx_change_file_access(n, a) chmod((const char *) n, a)

‌#define ngx_change_file_access_n "chmod()"

ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s);

‌#define ngx_set_file_time_n      "utimes()"

‌#define ngx_file_info(file, sb)  stat((const char *) file, sb)

‌#define ngx_file_info_n          "stat()"

‌#define ngx_fd_info(fd, sb)      fstat(fd, sb)

‌#define ngx_fd_info_n            "fstat()"

‌#define ngx_link_info(file, sb)  lstat((const char *) file, sb)

‌#define ngx_link_info_n          "lstat()"

‌#define ngx_is_dir(sb)           (S_ISDIR((sb)->st_mode))

‌#define ngx_is_file(sb)          (S_ISREG((sb)->st_mode))

‌#define ngx_is_link(sb)          (S_ISLNK((sb)->st_mode))

‌#define ngx_is_exec(sb)          (((sb)->st_mode & S_IXUSR) == S_IXUSR)

‌#define ngx_file_access(sb)      ((sb)->st_mode & 0777)

‌#define ngx_file_size(sb)        (sb)->st_size

‌#define ngx_file_fs_size(sb)     ngx_max((sb)->st_size, (sb)->st_blocks * 512)

‌#define ngx_file_mtime(sb)       (sb)->st_mtime

‌#define ngx_file_uniq(sb)        (sb)->st_ino

ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm);

void ngx_close_file_mapping(ngx_file_mapping_t *fm);

‌#define ngx_realpath(p, r)       (u_char *) realpath((char *) p, (char *) r)

‌#define ngx_realpath_n           "realpath()"

‌#define ngx_getcwd(buf, size)    (getcwd((char *) buf, size) != NULL)

‌#define ngx_getcwd_n             "getcwd()"

‌#define ngx_path_separator(c)    ((c) == '/')

#if defined(PATH_MAX)

‌#define NGX_HAVE_MAX_PATH        1

‌#define NGX_MAX_PATH             PATH_MAX

#else

‌#define NGX_MAX_PATH             4096

#endif

‌#define NGX_DIR_MASK_LEN         0

ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir);

‌#define ngx_open_dir_n           "opendir()"

‌#define ngx_close_dir(d)         closedir((d)->dir)

‌#define ngx_close_dir_n          "closedir()"

ngx_int_t ngx_read_dir(ngx_dir_t *dir);

‌#define ngx_read_dir_n           "readdir()"

‌#define ngx_create_dir(name, access) mkdir((const char *) name, access)

‌#define ngx_create_dir_n         "mkdir()"

‌#define ngx_delete_dir(name)     rmdir((const char *) name)

‌#define ngx_delete_dir_n         "rmdir()"

‌#define ngx_dir_access(a)        (a | (a & 0444) >> 2)

‌#define ngx_de_name(dir)         ((u_char *) (dir)->de->d_name)

#if (NGX_HAVE_D_NAMLEN)

‌#define ngx_de_namelen(dir)      (dir)->de->d_namlen

#else

‌#define ngx_de_namelen(dir)      ngx_strlen((dir)->de->d_name)

#endif

static ngx_inline ngx_int_t

‌ngx_de_info(u_char *name, ngx_dir_t *dir)

{

    dir->type = 0;

    return stat((const char *) name, &dir->info);

}

‌#define ngx_de_info_n            "stat()"

‌#define ngx_de_link_info(name, dir)  lstat((const char *) name, &(dir)->info)

‌#define ngx_de_link_info_n       "lstat()"

#if (NGX_HAVE_D_TYPE)

/*

* some file systems (e.g. XFS on Linux and CD9660 on FreeBSD)

* do not set dirent.d_type

*/

‌#define ngx_de_is_dir(dir)                                                   \

    (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))

‌#define ngx_de_is_file(dir)                                                  \

    (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))

‌#define ngx_de_is_link(dir)                                                  \

    (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode)))

#else

‌#define ngx_de_is_dir(dir)       (S_ISDIR((dir)->info.st_mode))

‌#define ngx_de_is_file(dir)      (S_ISREG((dir)->info.st_mode))

‌#define ngx_de_is_link(dir)      (S_ISLNK((dir)->info.st_mode))

#endif

‌#define ngx_de_access(dir)       (((dir)->info.st_mode) & 0777)

‌#define ngx_de_size(dir)         (dir)->info.st_size

‌#define ngx_de_fs_size(dir)                                                  \

    ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512)

‌#define ngx_de_mtime(dir)        (dir)->info.st_mtime

ngx_int_t ngx_open_glob(ngx_glob_t *gl);

‌#define ngx_open_glob_n          "glob()"

ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);

void ngx_close_glob(ngx_glob_t *gl);

ngx_err_t ngx_trylock_fd(ngx_fd_t fd);

ngx_err_t ngx_lock_fd(ngx_fd_t fd);

ngx_err_t ngx_unlock_fd(ngx_fd_t fd);

‌#define ngx_trylock_fd_n         "fcntl(F_SETLK, F_WRLCK)"

‌#define ngx_lock_fd_n            "fcntl(F_SETLKW, F_WRLCK)"

‌#define ngx_unlock_fd_n          "fcntl(F_SETLK, F_UNLCK)"

#if (NGX_HAVE_F_READAHEAD)

‌#define NGX_HAVE_READ_AHEAD      1

‌#define ngx_read_ahead(fd, n)    fcntl(fd, F_READAHEAD, (int) n)

‌#define ngx_read_ahead_n         "fcntl(fd, F_READAHEAD)"

#elif (NGX_HAVE_POSIX_FADVISE)

‌#define NGX_HAVE_READ_AHEAD      1

ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n);

‌#define ngx_read_ahead_n         "posix_fadvise(POSIX_FADV_SEQUENTIAL)"

#else

‌#define ngx_read_ahead(fd, n)    0

‌#define ngx_read_ahead_n         "ngx_read_ahead_n"

#endif

#if (NGX_HAVE_O_DIRECT)

ngx_int_t ngx_directio_on(ngx_fd_t fd);

‌#define ngx_directio_on_n        "fcntl(O_DIRECT)"

ngx_int_t ngx_directio_off(ngx_fd_t fd);

‌#define ngx_directio_off_n       "fcntl(!O_DIRECT)"

#elif (NGX_HAVE_F_NOCACHE)

‌#define ngx_directio_on(fd)      fcntl(fd, F_NOCACHE, 1)

‌#define ngx_directio_on_n        "fcntl(F_NOCACHE, 1)"

#elif (NGX_HAVE_DIRECTIO)

‌#define ngx_directio_on(fd)      directio(fd, DIRECTIO_ON)

‌#define ngx_directio_on_n        "directio(DIRECTIO_ON)"

#else

‌#define ngx_directio_on(fd)      0

‌#define ngx_directio_on_n        "ngx_directio_on_n"

#endif

size_t ngx_fs_bsize(u_char *name);

#if (NGX_HAVE_OPENAT)

‌#define ngx_openat_file(fd, name, mode, create, access)                      \

    openat(fd, (const char *) name, mode|create, access)

‌#define ngx_openat_file_n        "openat()"

‌#define ngx_file_at_info(fd, name, sb, flag)                                 \

    fstatat(fd, (const char *) name, sb, flag)

‌#define ngx_file_at_info_n       "fstatat()"

‌#define NGX_AT_FDCWD             (ngx_fd_t) AT_FDCWD

#endif

‌#define ngx_stderr               STDERR_FILENO

‌#define ngx_set_stderr(fd)       dup2(fd, STDERR_FILENO)

‌#define ngx_set_stderr_n         "dup2(STDERR_FILENO)"

#if (NGX_HAVE_FILE_AIO)

ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size,

    off_t offset, ngx_pool_t *pool);

extern ngx_uint_t  ngx_file_aio;

#endif

#endif /* _NGX_FILES_H_INCLUDED_ */

src/os/unix/ngx_files.c - nginx-1.7.9

Global variables defined

ngx_file_aio

Functions defined

ngx_close_file_mapping

ngx_close_glob

ngx_create_file_mapping

ngx_directio_off

ngx_directio_on

ngx_fs_bsize

ngx_fs_bsize

ngx_fs_bsize

ngx_lock_fd

ngx_open_dir

ngx_open_glob

ngx_open_tempfile

ngx_read_ahead

ngx_read_dir

ngx_read_file

ngx_read_glob

ngx_set_file_time

ngx_trylock_fd

ngx_unlock_fd

ngx_write_chain_to_file

ngx_write_file

Macros defined

NGX_IOVS

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_FILE_AIO)

‌ngx_uint_t  ngx_file_aio = 1;

#endif

ssize_t

‌ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)

{

    ssize_t  n;

    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "read: %d, %p, %uz, %O", file->fd, buf, size, offset);

#if (NGX_HAVE_PREAD)

    n = pread(file->fd, buf, size, offset);

    if (n == -1) {

        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                      "pread() \"%s\" failed", file->name.data);

        return NGX_ERROR;

    }

#else

    if (file->sys_offset != offset) {

        if (lseek(file->fd, offset, SEEK_SET) == -1) {

            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                          "lseek() \"%s\" failed", file->name.data);

            return NGX_ERROR;

        }

        file->sys_offset = offset;

    }

    n = read(file->fd, buf, size);

    if (n == -1) {

        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                      "read() \"%s\" failed", file->name.data);

        return NGX_ERROR;

    }

    file->sys_offset += n;

#endif

    file->offset += n;

    return n;

}

ssize_t

‌ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)

{

    ssize_t  n, written;

    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "write: %d, %p, %uz, %O", file->fd, buf, size, offset);

    written = 0;

#if (NGX_HAVE_PWRITE)

    for ( ;; ) {

        n = pwrite(file->fd, buf + written, size, offset);

        if (n == -1) {

            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                          "pwrite() \"%s\" failed", file->name.data);

            return NGX_ERROR;

        }

        file->offset += n;

        written += n;

        if ((size_t) n == size) {

            return written;

        }

        offset += n;

        size -= n;

    }

#else

    if (file->sys_offset != offset) {

        if (lseek(file->fd, offset, SEEK_SET) == -1) {

            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                          "lseek() \"%s\" failed", file->name.data);

            return NGX_ERROR;

        }

        file->sys_offset = offset;

    }

    for ( ;; ) {

        n = write(file->fd, buf + written, size);

        if (n == -1) {

            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                          "write() \"%s\" failed", file->name.data);

            return NGX_ERROR;

        }

        file->offset += n;

        written += n;

        if ((size_t) n == size) {

            return written;

        }

        size -= n;

    }

#endif

}

ngx_fd_t

‌ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access)

{

    ngx_fd_t  fd;

    fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR,

              access ? access : 0600);

    if (fd != -1 && !persistent) {

        (void) unlink((const char *) name);

    }

    return fd;

}

‌#define NGX_IOVS  8

ssize_t

‌ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,

    ngx_pool_t *pool)

{

    u_char        *prev;

    size_t         size;

    ssize_t        total, n;

    ngx_array_t    vec;

    struct iovec  *iov, iovs[NGX_IOVS];

    /* use pwrite() if there is the only buf in a chain */

    if (cl->next == NULL) {

        return ngx_write_file(file, cl->buf->pos,

                              (size_t) (cl->buf->last - cl->buf->pos),

                              offset);

    }

    total = 0;

    vec.elts = iovs;

    vec.size = sizeof(struct iovec);

    vec.nalloc = NGX_IOVS;

    vec.pool = pool;

    do {

        prev = NULL;

        iov = NULL;

        size = 0;

        vec.nelts = 0;

        /* create the iovec and coalesce the neighbouring bufs */

        while (cl && vec.nelts < IOV_MAX) {

            if (prev == cl->buf->pos) {

                iov->iov_len += cl->buf->last - cl->buf->pos;

            } else {

                iov = ngx_array_push(&vec);

                if (iov == NULL) {

                    return NGX_ERROR;

                }

                iov->iov_base = (void *) cl->buf->pos;

                iov->iov_len = cl->buf->last - cl->buf->pos;

            }

            size += cl->buf->last - cl->buf->pos;

            prev = cl->buf->last;

            cl = cl->next;

        }

        /* use pwrite() if there is the only iovec buffer */

        if (vec.nelts == 1) {

            iov = vec.elts;

            n = ngx_write_file(file, (u_char *) iov[0].iov_base,

                               iov[0].iov_len, offset);

            if (n == NGX_ERROR) {

                return n;

            }

            return total + n;

        }

        if (file->sys_offset != offset) {

            if (lseek(file->fd, offset, SEEK_SET) == -1) {

                ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                              "lseek() \"%s\" failed", file->name.data);

                return NGX_ERROR;

            }

            file->sys_offset = offset;

        }

        n = writev(file->fd, vec.elts, vec.nelts);

        if (n == -1) {

            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                          "writev() \"%s\" failed", file->name.data);

            return NGX_ERROR;

        }

        if ((size_t) n != size) {

            ngx_log_error(NGX_LOG_CRIT, file->log, 0,

                          "writev() \"%s\" has written only %z of %uz",

                          file->name.data, n, size);

            return NGX_ERROR;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,

                       "writev: %d, %z", file->fd, n);

        file->sys_offset += n;

        file->offset += n;

        offset += n;

        total += n;

    } while (cl);

    return total;

}

ngx_int_t

‌ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s)

{

    struct timeval  tv[2];

    tv[0].tv_sec = ngx_time();

    tv[0].tv_usec = 0;

    tv[1].tv_sec = s;

    tv[1].tv_usec = 0;

    if (utimes((char *) name, tv) != -1) {

        return NGX_OK;

    }

    return NGX_ERROR;

}

ngx_int_t

‌ngx_create_file_mapping(ngx_file_mapping_t *fm)

{

    fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE,

                           NGX_FILE_DEFAULT_ACCESS);

    if (fm->fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", fm->name);

        return NGX_ERROR;

    }

    if (ftruncate(fm->fd, fm->size) == -1) {

        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,

                      "ftruncate() \"%s\" failed", fm->name);

        goto failed;

    }

    fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED,

                    fm->fd, 0);

    if (fm->addr != MAP_FAILED) {

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,

                  "mmap(%uz) \"%s\" failed", fm->size, fm->name);

failed:

    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", fm->name);

    }

    return NGX_ERROR;

}

void

‌ngx_close_file_mapping(ngx_file_mapping_t *fm)

{

    if (munmap(fm->addr, fm->size) == -1) {

        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,

                      "munmap(%uz) \"%s\" failed", fm->size, fm->name);

    }

    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", fm->name);

    }

}

ngx_int_t

‌ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)

{

    dir->dir = opendir((const char *) name->data);

    if (dir->dir == NULL) {

        return NGX_ERROR;

    }

    dir->valid_info = 0;

    return NGX_OK;

}

ngx_int_t

‌ngx_read_dir(ngx_dir_t *dir)

{

    dir->de = readdir(dir->dir);

    if (dir->de) {

#if (NGX_HAVE_D_TYPE)

        dir->type = dir->de->d_type;

#else

        dir->type = 0;

#endif

        return NGX_OK;

    }

    return NGX_ERROR;

}

ngx_int_t

‌ngx_open_glob(ngx_glob_t *gl)

{

    int  n;

    n = glob((char *) gl->pattern, 0, NULL, &gl->pglob);

    if (n == 0) {

        return NGX_OK;

    }

#ifdef GLOB_NOMATCH

    if (n == GLOB_NOMATCH && gl->test) {

        return NGX_OK;

    }

#endif

    return NGX_ERROR;

}

ngx_int_t

‌ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name)

{

    size_t  count;

#ifdef GLOB_NOMATCH

    count = (size_t) gl->pglob.gl_pathc;

#else

    count = (size_t) gl->pglob.gl_matchc;

#endif

    if (gl->n < count) {

        name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]);

        name->data = (u_char *) gl->pglob.gl_pathv[gl->n];

        gl->n++;

        return NGX_OK;

    }

    return NGX_DONE;

}

void

‌ngx_close_glob(ngx_glob_t *gl)

{

    globfree(&gl->pglob);

}

ngx_err_t

‌ngx_trylock_fd(ngx_fd_t fd)

{

    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));

    fl.l_type = F_WRLCK;

    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLK, &fl) == -1) {

        return ngx_errno;

    }

    return 0;

}

ngx_err_t

‌ngx_lock_fd(ngx_fd_t fd)

{

    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));

    fl.l_type = F_WRLCK;

    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLKW, &fl) == -1) {

        return ngx_errno;

    }

    return 0;

}

ngx_err_t

‌ngx_unlock_fd(ngx_fd_t fd)

{

    struct flock  fl;

    ngx_memzero(&fl, sizeof(struct flock));

    fl.l_type = F_UNLCK;

    fl.l_whence = SEEK_SET;

    if (fcntl(fd, F_SETLK, &fl) == -1) {

        return  ngx_errno;

    }

    return 0;

}

#if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD)

ngx_int_t

‌ngx_read_ahead(ngx_fd_t fd, size_t n)

{

    int  err;

    err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);

    if (err == 0) {

        return 0;

    }

    ngx_set_errno(err);

    return NGX_FILE_ERROR;

}

#endif

#if (NGX_HAVE_O_DIRECT)

ngx_int_t

‌ngx_directio_on(ngx_fd_t fd)

{

    int  flags;

    flags = fcntl(fd, F_GETFL);

    if (flags == -1) {

        return NGX_FILE_ERROR;

    }

    return fcntl(fd, F_SETFL, flags | O_DIRECT);

}

ngx_int_t

‌ngx_directio_off(ngx_fd_t fd)

{

    int  flags;

    flags = fcntl(fd, F_GETFL);

    if (flags == -1) {

        return NGX_FILE_ERROR;

    }

    return fcntl(fd, F_SETFL, flags & ~O_DIRECT);

}

#endif

#if (NGX_HAVE_STATFS)

size_t

‌ngx_fs_bsize(u_char *name)

{

    struct statfs  fs;

    if (statfs((char *) name, &fs) == -1) {

        return 512;

    }

    if ((fs.f_bsize % 512) != 0) {

        return 512;

    }

    return (size_t) fs.f_bsize;

}

#elif (NGX_HAVE_STATVFS)

size_t

‌ngx_fs_bsize(u_char *name)

{

    struct statvfs  fs;

    if (statvfs((char *) name, &fs) == -1) {

        return 512;

    }

    if ((fs.f_frsize % 512) != 0) {

        return 512;

    }

    return (size_t) fs.f_frsize;

}

#else

size_t

‌ngx_fs_bsize(u_char *name)

{

    return 512;

}

#endif

src/core/ngx_log.h - nginx-1.7.9

Data types defined

ngx_log_handler_pt

ngx_log_s

ngx_log_writer_pt

Functions defined

ngx_write_stderr

Macros defined

NGX_HAVE_VARIADIC_MACROS

NGX_HAVE_VARIADIC_MACROS

NGX_HAVE_VARIADIC_MACROS

NGX_LOG_ALERT

NGX_LOG_CRIT

NGX_LOG_DEBUG

NGX_LOG_DEBUG_ALL

NGX_LOG_DEBUG_ALLOC

NGX_LOG_DEBUG_CONNECTION

NGX_LOG_DEBUG_CORE

NGX_LOG_DEBUG_EVENT

NGX_LOG_DEBUG_FIRST

NGX_LOG_DEBUG_HTTP

NGX_LOG_DEBUG_LAST

NGX_LOG_DEBUG_MAIL

NGX_LOG_DEBUG_MUTEX

NGX_LOG_DEBUG_MYSQL

NGX_LOG_EMERG

NGX_LOG_ERR

NGX_LOG_INFO

NGX_LOG_NOTICE

NGX_LOG_STDERR

NGX_LOG_WARN

NGX_MAX_ERROR_STR

_NGX_LOG_H_INCLUDED_

ngx_log_debug

ngx_log_debug

ngx_log_debug0

ngx_log_debug0

ngx_log_debug0

ngx_log_debug1

ngx_log_debug1

ngx_log_debug1

ngx_log_debug2

ngx_log_debug2

ngx_log_debug2

ngx_log_debug3

ngx_log_debug3

ngx_log_debug3

ngx_log_debug4

ngx_log_debug4

ngx_log_debug4

ngx_log_debug5

ngx_log_debug5

ngx_log_debug5

ngx_log_debug6

ngx_log_debug6

ngx_log_debug6

ngx_log_debug7

ngx_log_debug7

ngx_log_debug7

ngx_log_debug8

ngx_log_debug8

ngx_log_debug8

ngx_log_error

ngx_log_error

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_LOG_H_INCLUDED_

‌#define _NGX_LOG_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_LOG_STDERR            0

‌#define NGX_LOG_EMERG             1

‌#define NGX_LOG_ALERT             2

‌#define NGX_LOG_CRIT              3

‌#define NGX_LOG_ERR               4

‌#define NGX_LOG_WARN              5

‌#define NGX_LOG_NOTICE            6

‌#define NGX_LOG_INFO              7

‌#define NGX_LOG_DEBUG             8

‌#define NGX_LOG_DEBUG_CORE        0x010

‌#define NGX_LOG_DEBUG_ALLOC       0x020

‌#define NGX_LOG_DEBUG_MUTEX       0x040

‌#define NGX_LOG_DEBUG_EVENT       0x080

‌#define NGX_LOG_DEBUG_HTTP        0x100

‌#define NGX_LOG_DEBUG_MAIL        0x200

‌#define NGX_LOG_DEBUG_MYSQL       0x400

/*

* do not forget to update debug_levels[] in src/core/ngx_log.c

* after the adding a new debug level

*/

‌#define NGX_LOG_DEBUG_FIRST       NGX_LOG_DEBUG_CORE

‌#define NGX_LOG_DEBUG_LAST        NGX_LOG_DEBUG_MYSQL

‌#define NGX_LOG_DEBUG_CONNECTION  0x80000000

‌#define NGX_LOG_DEBUG_ALL         0x7ffffff0

‌typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);

‌typedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level,

    u_char *buf, size_t len);

‌struct ngx_log_s {

    ngx_uint_t           log_level;

    ngx_open_file_t     *file;

    ngx_atomic_uint_t    connection;

    ngx_log_handler_pt   handler;

    void                *data;

    ngx_log_writer_pt    writer;

    void                *wdata;

    /*

     * we declare "action" as "char *" because the actions are usually

     * the static strings and in the "u_char *" case we have to override

     * their types all the time

     */

    char                *action;

    ngx_log_t           *next;

};

‌#define NGX_MAX_ERROR_STR   2048

/*********************************/

#if (NGX_HAVE_C99_VARIADIC_MACROS)

‌#define NGX_HAVE_VARIADIC_MACROS  1

‌#define ngx_log_error(level, log, ...)                                        \

    if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__)

void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...);

‌#define ngx_log_debug(level, log, ...)                                        \

    if ((log)->log_level & level)                                             \

        ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__)

/*********************************/

#elif (NGX_HAVE_GCC_VARIADIC_MACROS)

‌#define NGX_HAVE_VARIADIC_MACROS  1

‌#define ngx_log_error(level, log, args...)                                    \

    if ((log)->log_level >= level) ngx_log_error_core(level, log, args)

void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...);

‌#define ngx_log_debug(level, log, args...)                                    \

    if ((log)->log_level & level)                                             \

        ngx_log_error_core(NGX_LOG_DEBUG, log, args)

/*********************************/

#else /* NO VARIADIC MACROS */

‌#define NGX_HAVE_VARIADIC_MACROS  0

void ngx_cdecl ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...);

void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, va_list args);

void ngx_cdecl ngx_log_debug_core(ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...);

#endif /* VARIADIC MACROS */

/*********************************/

#if (NGX_DEBUG)

#if (NGX_HAVE_VARIADIC_MACROS)

‌#define ngx_log_debug0(level, log, err, fmt)                                  \

        ngx_log_debug(level, log, err, fmt)

‌#define ngx_log_debug1(level, log, err, fmt, arg1)                            \

        ngx_log_debug(level, log, err, fmt, arg1)

‌#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \

        ngx_log_debug(level, log, err, fmt, arg1, arg2)

‌#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \

        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3)

‌#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \

        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4)

‌#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \

        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)

‌#define ngx_log_debug6(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6)                    \

        ngx_log_debug(level, log, err, fmt,                                   \

                       arg1, arg2, arg3, arg4, arg5, arg6)

‌#define ngx_log_debug7(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \

        ngx_log_debug(level, log, err, fmt,                                   \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)

‌#define ngx_log_debug8(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \

        ngx_log_debug(level, log, err, fmt,                                   \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)

#else /* NO VARIADIC MACROS */

‌#define ngx_log_debug0(level, log, err, fmt)                                  \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt)

‌#define ngx_log_debug1(level, log, err, fmt, arg1)                            \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1)

‌#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1, arg2)

‌#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3)

‌#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4)

‌#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5)

‌#define ngx_log_debug6(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6)                    \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)

‌#define ngx_log_debug7(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt,                                     \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)

‌#define ngx_log_debug8(level, log, err, fmt,                                  \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \

    if ((log)->log_level & level)                                             \

        ngx_log_debug_core(log, err, fmt,                                     \

                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)

#endif

#else /* NO NGX_DEBUG */

‌#define ngx_log_debug0(level, log, err, fmt)

‌#define ngx_log_debug1(level, log, err, fmt, arg1)

‌#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)

‌#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)

‌#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)

‌#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)

‌#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)

‌#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \

                       arg6, arg7)

‌#define ngx_log_debug8(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \

                       arg6, arg7, arg8)

#endif

/*********************************/

ngx_log_t *ngx_log_init(u_char *prefix);

void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);

void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);

u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);

ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle);

ngx_int_t ngx_log_redirect_stderr(ngx_cycle_t *cycle);

ngx_log_t *ngx_log_get_file_log(ngx_log_t *head);

char *ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head);

/*

* ngx_write_stderr() cannot be implemented as macro, since

* MSVC does not allow to use #ifdef inside macro parameters.

*

* ngx_write_fd() is used instead of ngx_write_console(), since

* CharToOemBuff() inside ngx_write_console() cannot be used with

* read only buffer as destination and CharToOemBuff() is not needed

* for ngx_write_stderr() anyway.

*/

static ngx_inline void

‌ngx_write_stderr(char *text)

{

    (void) ngx_write_fd(ngx_stderr, text, ngx_strlen(text));

}

extern ngx_module_t  ngx_errlog_module;

extern ngx_uint_t    ngx_use_stderr;

#endif /* _NGX_LOG_H_INCLUDED_ */

src/core/ngx_log.c - nginx-1.7.9

Global variables defined

debug_levels

err_levels

ngx_errlog_commands

ngx_errlog_module

ngx_errlog_module_ctx

ngx_log

ngx_log_file

ngx_use_stderr

Functions defined

ngx_error_log

ngx_log_abort

ngx_log_debug_core

ngx_log_errno

ngx_log_error

ngx_log_error_core

ngx_log_get_file_log

ngx_log_init

ngx_log_insert

ngx_log_open_default

ngx_log_redirect_stderr

ngx_log_set_levels

ngx_log_set_log

ngx_log_stderr

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);

static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log);

‌static ngx_command_t  ngx_errlog_commands[] = {

    {ngx_string("error_log"),

     NGX_MAIN_CONF|NGX_CONF_1MORE,

     ngx_error_log,

     0,

     0,

     NULL},

    ngx_null_command

};

‌static ngx_core_module_t  ngx_errlog_module_ctx = {

    ngx_string("errlog"),

    NULL,

    NULL

};

‌ngx_module_t  ngx_errlog_module = {

    NGX_MODULE_V1,

    &ngx_errlog_module_ctx,                /* module context */

    ngx_errlog_commands,                   /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_log_t        ngx_log;

‌static ngx_open_file_t  ngx_log_file;

‌ngx_uint_t              ngx_use_stderr = 1;

‌static ngx_str_t err_levels[] = {

    ngx_null_string,

    ngx_string("emerg"),

    ngx_string("alert"),

    ngx_string("crit"),

    ngx_string("error"),

    ngx_string("warn"),

    ngx_string("notice"),

    ngx_string("info"),

    ngx_string("debug")

};

‌static const char *debug_levels[] = {

    "debug_core", "debug_alloc", "debug_mutex", "debug_event",

    "debug_http", "debug_mail", "debug_mysql"

};

#if (NGX_HAVE_VARIADIC_MACROS)

void

‌ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...)

#else

void

ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, va_list args)

#endif

{

#if (NGX_HAVE_VARIADIC_MACROS)

    va_list      args;

#endif

    u_char      *p, *last, *msg;

    u_char       errstr[NGX_MAX_ERROR_STR];

    ngx_uint_t   wrote_stderr, debug_connection;

    last = errstr + NGX_MAX_ERROR_STR;

    ngx_memcpy(errstr, ngx_cached_err_log_time.data,

               ngx_cached_err_log_time.len);

    p = errstr + ngx_cached_err_log_time.len;

    p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);

    /* pid#tid */

    p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",

                    ngx_log_pid, ngx_log_tid);

    if (log->connection) {

        p = ngx_slprintf(p, last, "*%uA ", log->connection);

    }

    msg = p;

#if (NGX_HAVE_VARIADIC_MACROS)

    va_start(args, fmt);

    p = ngx_vslprintf(p, last, fmt, args);

    va_end(args);

#else

    p = ngx_vslprintf(p, last, fmt, args);

#endif

    if (err) {

        p = ngx_log_errno(p, last, err);

    }

    if (level != NGX_LOG_DEBUG && log->handler) {

        p = log->handler(log, p, last - p);

    }

    if (p > last - NGX_LINEFEED_SIZE) {

        p = last - NGX_LINEFEED_SIZE;

    }

    ngx_linefeed(p);

    wrote_stderr = 0;

    debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0;

    while (log) {

        if (log->log_level < level && !debug_connection) {

            break;

        }

        if (log->writer) {

            log->writer(log, level, errstr, p - errstr);

            log = log->next;

            continue;

        }

        (void) ngx_write_fd(log->file->fd, errstr, p - errstr);

        if (log->file->fd == ngx_stderr) {

            wrote_stderr = 1;

        }

        log = log->next;

    }

    if (!ngx_use_stderr

        || level > NGX_LOG_WARN

        || wrote_stderr)

    {

        return;

    }

    msg -= (7 + err_levels[level].len + 3);

    (void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]);

    (void) ngx_write_console(ngx_stderr, msg, p - msg);

}

#if !(NGX_HAVE_VARIADIC_MACROS)

void ngx_cdecl

‌ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    const char *fmt, ...)

{

    va_list  args;

    if (log->log_level >= level) {

        va_start(args, fmt);

        ngx_log_error_core(level, log, err, fmt, args);

        va_end(args);

    }

}

void ngx_cdecl

‌ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...)

{

    va_list  args;

    va_start(args, fmt);

    ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args);

    va_end(args);

}

#endif

void ngx_cdecl

‌ngx_log_abort(ngx_err_t err, const char *fmt, ...)

{

    u_char   *p;

    va_list   args;

    u_char    errstr[NGX_MAX_CONF_ERRSTR];

    va_start(args, fmt);

    p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);

    va_end(args);

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,

                  "%*s", p - errstr, errstr);

}

void ngx_cdecl

‌ngx_log_stderr(ngx_err_t err, const char *fmt, ...)

{

    u_char   *p, *last;

    va_list   args;

    u_char    errstr[NGX_MAX_ERROR_STR];

    last = errstr + NGX_MAX_ERROR_STR;

    p = errstr + 7;

    ngx_memcpy(errstr, "nginx: ", 7);

    va_start(args, fmt);

    p = ngx_vslprintf(p, last, fmt, args);

    va_end(args);

    if (err) {

        p = ngx_log_errno(p, last, err);

    }

    if (p > last - NGX_LINEFEED_SIZE) {

        p = last - NGX_LINEFEED_SIZE;

    }

    ngx_linefeed(p);

    (void) ngx_write_console(ngx_stderr, errstr, p - errstr);

}

u_char *

‌ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)

{

    if (buf > last - 50) {

        /* leave a space for an error code */

        buf = last - 50;

        *buf++ = '.';

        *buf++ = '.';

        *buf++ = '.';

    }

#if (NGX_WIN32)

    buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)

                                       ? " (%d: " : " (%Xd: ", err);

#else

    buf = ngx_slprintf(buf, last, " (%d: ", err);

#endif

    buf = ngx_strerror(err, buf, last - buf);

    if (buf < last) {

        *buf++ = ')';

    }

    return buf;

}

ngx_log_t *

‌ngx_log_init(u_char *prefix)

{

    u_char  *p, *name;

    size_t   nlen, plen;

    ngx_log.file = &ngx_log_file;

    ngx_log.log_level = NGX_LOG_NOTICE;

    name = (u_char *) NGX_ERROR_LOG_PATH;

    /*

     * we use ngx_strlen() here since BCC warns about

     * condition is always false and unreachable code

     */

    nlen = ngx_strlen(name);

    if (nlen == 0) {

        ngx_log_file.fd = ngx_stderr;

        return &ngx_log;

    }

    p = NULL;

#if (NGX_WIN32)

    if (name[1] != ':') {

#else

    if (name[0] != '/') {

#endif

        if (prefix) {

            plen = ngx_strlen(prefix);

        } else {

#ifdef NGX_PREFIX

            prefix = (u_char *) NGX_PREFIX;

            plen = ngx_strlen(prefix);

#else

            plen = 0;

#endif

        }

        if (plen) {

            name = malloc(plen + nlen + 2);

            if (name == NULL) {

                return NULL;

            }

            p = ngx_cpymem(name, prefix, plen);

            if (!ngx_path_separator(*(p - 1))) {

                *p++ = '/';

            }

            ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);

            p = name;

        }

    }

    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,

                                    NGX_FILE_CREATE_OR_OPEN,

                                    NGX_FILE_DEFAULT_ACCESS);

    if (ngx_log_file.fd == NGX_INVALID_FILE) {

        ngx_log_stderr(ngx_errno,

                       "[alert] could not open error log file: "

                       ngx_open_file_n " \"%s\" failed", name);

#if (NGX_WIN32)

        ngx_event_log(ngx_errno,

                       "could not open error log file: "

                       ngx_open_file_n " \"%s\" failed", name);

#endif

        ngx_log_file.fd = ngx_stderr;

    }

    if (p) {

        ngx_free(p);

    }

    return &ngx_log;

}

ngx_int_t

‌ngx_log_open_default(ngx_cycle_t *cycle)

{

    ngx_log_t         *log;

    static ngx_str_t   error_log = ngx_string(NGX_ERROR_LOG_PATH);

    if (ngx_log_get_file_log(&cycle->new_log) != NULL) {

        return NGX_OK;

    }

    if (cycle->new_log.log_level != 0) {

        /* there are some error logs, but no files */

        log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));

        if (log == NULL) {

            return NGX_ERROR;

        }

    } else {

        /* no error logs at all */

        log = &cycle->new_log;

    }

    log->log_level = NGX_LOG_ERR;

    log->file = ngx_conf_open_file(cycle, &error_log);

    if (log->file == NULL) {

        return NGX_ERROR;

    }

    if (log != &cycle->new_log) {

        ngx_log_insert(&cycle->new_log, log);

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_log_redirect_stderr(ngx_cycle_t *cycle)

{

    ngx_fd_t  fd;

    if (cycle->log_use_stderr) {

        return NGX_OK;

    }

    /* file log always exists when we are called */

    fd = ngx_log_get_file_log(cycle->log)->file->fd;

    if (fd != ngx_stderr) {

        if (ngx_set_stderr(fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          ngx_set_stderr_n " failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

ngx_log_t *

‌ngx_log_get_file_log(ngx_log_t *head)

{

    ngx_log_t  *log;

    for (log = head; log; log = log->next) {

        if (log->file != NULL) {

            return log;

        }

    }

    return NULL;

}

static char *

‌ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)

{

    ngx_uint_t   i, n, d, found;

    ngx_str_t   *value;

    if (cf->args->nelts == 2) {

        log->log_level = NGX_LOG_ERR;

        return NGX_CONF_OK;

    }

    value = cf->args->elts;

    for (i = 2; i < cf->args->nelts; i++) {

        found = 0;

        for (n = 1; n <= NGX_LOG_DEBUG; n++) {

            if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {

                if (log->log_level != 0) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "duplicate log level \"%V\"",

                                       &value[i]);

                    return NGX_CONF_ERROR;

                }

                log->log_level = n;

                found = 1;

                break;

            }

        }

        for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) {

            if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {

                if (log->log_level & ~NGX_LOG_DEBUG_ALL) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "invalid log level \"%V\"",

                                       &value[i]);

                    return NGX_CONF_ERROR;

                }

                log->log_level |= d;

                found = 1;

                break;

            }

        }

        if (!found) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid log level \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

    }

    if (log->log_level == NGX_LOG_DEBUG) {

        log->log_level = NGX_LOG_DEBUG_ALL;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_log_t  *dummy;

    dummy = &cf->cycle->new_log;

    return ngx_log_set_log(cf, &dummy);

}

char *

‌ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head)

{

    ngx_log_t          *new_log;

    ngx_str_t          *value, name;

    ngx_syslog_peer_t  *peer;

    if (*head != NULL && (*head)->log_level == 0) {

        new_log = *head;

    } else {

        new_log = ngx_pcalloc(cf->pool, sizeof(ngx_log_t));

        if (new_log == NULL) {

            return NGX_CONF_ERROR;

        }

        if (*head == NULL) {

            *head = new_log;

        }

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "stderr") == 0) {

        ngx_str_null(&name);

        cf->cycle->log_use_stderr = 1;

        new_log->file = ngx_conf_open_file(cf->cycle, &name);

        if (new_log->file == NULL) {

            return NGX_CONF_ERROR;

        }

     } else if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {

        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));

        if (peer == NULL) {

            return NGX_CONF_ERROR;

        }

        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

        new_log->writer = ngx_syslog_writer;

        new_log->wdata = peer;

    } else {

        new_log->file = ngx_conf_open_file(cf->cycle, &value[1]);

        if (new_log->file == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) {

        return NGX_CONF_ERROR;

    }

    if (*head != new_log) {

        ngx_log_insert(*head, new_log);

    }

    return NGX_CONF_OK;

}

static void

‌ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log)

{

    ngx_log_t  tmp;

    if (new_log->log_level > log->log_level) {

        /*

         * list head address is permanent, insert new log after

         * head and swap its contents with head

         */

        tmp = *log;

        *log = *new_log;

        *new_log = tmp;

        log->next = new_log;

        return;

    }

    while (log->next) {

        if (new_log->log_level > log->next->log_level) {

            new_log->next = log->next;

            log->next = new_log;

            return;

        }

        log = log->next;

    }

    log->next = new_log;

}

src/core/ngx_string.h - nginx-1.7.9

Data types defined

ngx_keyval_t

ngx_str_node_t

ngx_str_t

ngx_variable_value_t

Functions defined

ngx_copy

ngx_strlchr

Macros defined

NGX_ESCAPE_ARGS

NGX_ESCAPE_HTML

NGX_ESCAPE_MAIL_AUTH

NGX_ESCAPE_MEMCACHED

NGX_ESCAPE_REFRESH

NGX_ESCAPE_URI

NGX_ESCAPE_URI_COMPONENT

NGX_UNESCAPE_REDIRECT

NGX_UNESCAPE_URI

_NGX_STRING_H_INCLUDED_

ngx_base64_decoded_length

ngx_base64_encoded_length

ngx_copy

ngx_cpymem

ngx_cpymem

ngx_memcmp

ngx_memcpy

ngx_memmove

ngx_memset

ngx_memzero

ngx_movemem

ngx_null_string

ngx_qsort

ngx_str_null

ngx_str_set

ngx_strchr

ngx_strcmp

ngx_string

ngx_strlen

ngx_strncmp

ngx_strstr

ngx_tolower

ngx_toupper

ngx_value

ngx_value_helper

ngx_vsnprintf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_STRING_H_INCLUDED_

‌#define _NGX_STRING_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    size_t      len;

    u_char     *data;

‌} ngx_str_t;

typedef struct {

    ngx_str_t   key;

    ngx_str_t   value;

‌} ngx_keyval_t;

typedef struct {

    unsigned    len:28;

    unsigned    valid:1;

    unsigned    no_cacheable:1;

    unsigned    not_found:1;

    unsigned    escape:1;

    u_char     *data;

‌} ngx_variable_value_t;

‌#define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }

‌#define ngx_null_string     { 0, NULL }

‌#define ngx_str_set(str, text)                                               \

    (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text

‌#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL

‌#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)

‌#define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)

void ngx_strlow(u_char *dst, u_char *src, size_t n);

‌#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)

/* msvc and icc7 compile strcmp() to inline loop */

‌#define ngx_strcmp(s1, s2)  strcmp((const char *) s1, (const char *) s2)

‌#define ngx_strstr(s1, s2)  strstr((const char *) s1, (const char *) s2)

‌#define ngx_strlen(s)       strlen((const char *) s)

‌#define ngx_strchr(s1, c)   strchr((const char *) s1, (int) c)

static ngx_inline u_char *

‌ngx_strlchr(u_char *p, u_char *last, u_char c)

{

    while (p < last) {

        if (*p == c) {

            return p;

        }

        p++;

    }

    return NULL;

}

/*

* msvc and icc7 compile memset() to the inline "rep stos"

* while ZeroMemory() and bzero() are the calls.

* icc7 may also inline several mov's of a zeroed register for small blocks.

*/

‌#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)

‌#define ngx_memset(buf, c, n)     (void) memset(buf, c, n)

#if (NGX_MEMCPY_LIMIT)

void *ngx_memcpy(void *dst, const void *src, size_t n);

‌#define ngx_cpymem(dst, src, n)   (((u_char *) ngx_memcpy(dst, src, n)) + (n))

#else

/*

* gcc3, msvc, and icc7 compile memcpy() to the inline "rep movs".

* gcc3 compiles memcpy(d, s, 4) to the inline "mov"es.

* icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves.

*/

‌#define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)

‌#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))

#endif

#if ( __INTEL_COMPILER >= 800 )

/*

* the simple inline cycle copies the variable length strings up to 16

* bytes faster than icc8 autodetecting _intel_fast_memcpy()

*/

static ngx_inline u_char *

‌ngx_copy(u_char *dst, u_char *src, size_t len)

{

    if (len < 17) {

        while (len) {

            *dst++ = *src++;

            len--;

        }

        return dst;

    } else {

        return ngx_cpymem(dst, src, len);

    }

}

#else

‌#define ngx_copy                  ngx_cpymem

#endif

‌#define ngx_memmove(dst, src, n)   (void) memmove(dst, src, n)

‌#define ngx_movemem(dst, src, n)   (((u_char *) memmove(dst, src, n)) + (n))

/* msvc and icc7 compile memcmp() to the inline loop */

‌#define ngx_memcmp(s1, s2, n)  memcmp((const char *) s1, (const char *) s2, n)

u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n);

u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);

u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);

u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);

u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,

    ...);

u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);

‌#define ngx_vsnprintf(buf, max, fmt, args)                                   \

    ngx_vslprintf(buf, buf + (max), fmt, args)

ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);

ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);

u_char *ngx_strnstr(u_char *s1, char *s2, size_t n);

u_char *ngx_strstrn(u_char *s1, char *s2, size_t n);

u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n);

u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n);

ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);

ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);

ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);

ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);

ngx_int_t ngx_filename_cmp(u_char *s1, u_char *s2, size_t n);

ngx_int_t ngx_atoi(u_char *line, size_t n);

ngx_int_t ngx_atofp(u_char *line, size_t n, size_t point);

ssize_t ngx_atosz(u_char *line, size_t n);

off_t ngx_atoof(u_char *line, size_t n);

time_t ngx_atotm(u_char *line, size_t n);

ngx_int_t ngx_hextoi(u_char *line, size_t n);

u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len);

‌#define ngx_base64_encoded_length(len)  (((len + 2) / 3) * 4)

‌#define ngx_base64_decoded_length(len)  (((len + 3) / 4) * 3)

void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);

void ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src);

ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);

ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);

uint32_t ngx_utf8_decode(u_char **p, size_t n);

size_t ngx_utf8_length(u_char *p, size_t n);

u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);

‌#define NGX_ESCAPE_URI            0

‌#define NGX_ESCAPE_ARGS           1

‌#define NGX_ESCAPE_URI_COMPONENT  2

‌#define NGX_ESCAPE_HTML           3

‌#define NGX_ESCAPE_REFRESH        4

‌#define NGX_ESCAPE_MEMCACHED      5

‌#define NGX_ESCAPE_MAIL_AUTH      6

‌#define NGX_UNESCAPE_URI       1

‌#define NGX_UNESCAPE_REDIRECT  2

uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,

    ngx_uint_t type);

void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);

uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);

uintptr_t ngx_escape_json(u_char *dst, u_char *src, size_t size);

typedef struct {

    ngx_rbtree_node_t         node;

    ngx_str_t                 str;

‌} ngx_str_node_t;

void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

ngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name,

    uint32_t hash);

void ngx_sort(void *base, size_t n, size_t size,

    ngx_int_t (*cmp)(const void *, const void *));

‌#define ngx_qsort             qsort

‌#define ngx_value_helper(n)   #n

‌#define ngx_value(n)          ngx_value_helper(n)

#endif /* _NGX_STRING_H_INCLUDED_ */

src/core/ngx_string.c - nginx-1.7.9

Functions defined

ngx_atofp

ngx_atoi

ngx_atoof

ngx_atosz

ngx_atotm

ngx_cpystrn

ngx_decode_base64

ngx_decode_base64_internal

ngx_decode_base64url

ngx_dns_strcmp

ngx_encode_base64

ngx_encode_base64_internal

ngx_encode_base64url

ngx_escape_html

ngx_escape_json

ngx_escape_uri

ngx_filename_cmp

ngx_hex_dump

ngx_hextoi

ngx_memcpy

ngx_memn2cmp

ngx_pstrdup

ngx_rstrncasecmp

ngx_rstrncmp

ngx_slprintf

ngx_snprintf

ngx_sort

ngx_sprintf

ngx_sprintf_num

ngx_str_rbtree_insert_value

ngx_str_rbtree_lookup

ngx_strcasecmp

ngx_strcasestrn

ngx_strlcasestrn

ngx_strlow

ngx_strncasecmp

ngx_strnstr

ngx_strstrn

ngx_unescape_uri

ngx_utf8_cpystrn

ngx_utf8_decode

ngx_utf8_length

ngx_vslprintf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,

    u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);

static void ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src,

    const u_char *basis, ngx_uint_t padding);

static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src,

    const u_char *basis);

void

‌ngx_strlow(u_char *dst, u_char *src, size_t n)

{

    while (n) {

        *dst = ngx_tolower(*src);

        dst++;

        src++;

        n--;

    }

}

u_char *

‌ngx_cpystrn(u_char *dst, u_char *src, size_t n)

{

    if (n == 0) {

        return dst;

    }

    while (--n) {

        *dst = *src;

        if (*dst == '\0') {

            return dst;

        }

        dst++;

        src++;

    }

    *dst = '\0';

    return dst;

}

u_char *

‌ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)

{

    u_char  *dst;

    dst = ngx_pnalloc(pool, src->len);

    if (dst == NULL) {

        return NULL;

    }

    ngx_memcpy(dst, src->data, src->len);

    return dst;

}

/*

* supported formats:

*    %[0][width][x][X]O        off_t

*    %[0][width]T              time_t

*    %[0][width][u][x|X]z      ssize_t/size_t

*    %[0][width][u][x|X]d      int/u_int

*    %[0][width][u][x|X]l      long

*    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t

*    %[0][width][u][x|X]D      int32_t/uint32_t

*    %[0][width][u][x|X]L      int64_t/uint64_t

*    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t

*    %[0][width][.width]f      double, max valid number fits to %18.15f

*    %P                        ngx_pid_t

*    %M                        ngx_msec_t

*    %r                        rlim_t

*    %p                        void *

*    %V                        ngx_str_t *

*    %v                        ngx_variable_value_t *

*    %s                        null-terminated string

*    %*s                       length and string

*    %Z                        '\0'

*    %N                        '\n'

*    %c                        char

*    %%                        %

*

*  reserved:

*    %t                        ptrdiff_t

*    %S                        null-terminated wchar string

*    %C                        wchar

*/

u_char * ngx_cdecl

‌ngx_sprintf(u_char *buf, const char *fmt, ...)

{

    u_char   *p;

    va_list   args;

    va_start(args, fmt);

    p = ngx_vslprintf(buf, (void *) -1, fmt, args);

    va_end(args);

    return p;

}

u_char * ngx_cdecl

‌ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...)

{

    u_char   *p;

    va_list   args;

    va_start(args, fmt);

    p = ngx_vslprintf(buf, buf + max, fmt, args);

    va_end(args);

    return p;

}

u_char * ngx_cdecl

‌ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)

{

    u_char   *p;

    va_list   args;

    va_start(args, fmt);

    p = ngx_vslprintf(buf, last, fmt, args);

    va_end(args);

    return p;

}

u_char *

‌ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)

{

    u_char                *p, zero;

    int                    d;

    double                 f;

    size_t                 len, slen;

    int64_t                i64;

    uint64_t               ui64, frac;

    ngx_msec_t             ms;

    ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n;

    ngx_str_t             *v;

    ngx_variable_value_t  *vv;

    while (*fmt && buf < last) {

        /*

         * "buf < last" means that we could copy at least one character:

         * the plain character, "%%", "%c", and minus without the checking

         */

        if (*fmt == '%') {

            i64 = 0;

            ui64 = 0;

            zero = (u_char) ((*++fmt == '0') ? '0' : ' ');

            width = 0;

            sign = 1;

            hex = 0;

            max_width = 0;

            frac_width = 0;

            slen = (size_t) -1;

            while (*fmt >= '0' && *fmt <= '9') {

                width = width * 10 + *fmt++ - '0';

            }

            for ( ;; ) {

                switch (*fmt) {

                case 'u':

                    sign = 0;

                    fmt++;

                    continue;

                case 'm':

                    max_width = 1;

                    fmt++;

                    continue;

                case 'X':

                    hex = 2;

                    sign = 0;

                    fmt++;

                    continue;

                case 'x':

                    hex = 1;

                    sign = 0;

                    fmt++;

                    continue;

                case '.':

                    fmt++;

                    while (*fmt >= '0' && *fmt <= '9') {

                        frac_width = frac_width * 10 + *fmt++ - '0';

                    }

                    break;

                case '*':

                    slen = va_arg(args, size_t);

                    fmt++;

                    continue;

                default:

                    break;

                }

                break;

            }

            switch (*fmt) {

            case 'V':

                v = va_arg(args, ngx_str_t *);

                len = ngx_min(((size_t) (last - buf)), v->len);

                buf = ngx_cpymem(buf, v->data, len);

                fmt++;

                continue;

            case 'v':

                vv = va_arg(args, ngx_variable_value_t *);

                len = ngx_min(((size_t) (last - buf)), vv->len);

                buf = ngx_cpymem(buf, vv->data, len);

                fmt++;

                continue;

            case 's':

                p = va_arg(args, u_char *);

                if (slen == (size_t) -1) {

                    while (*p && buf < last) {

                        *buf++ = *p++;

                    }

                } else {

                    len = ngx_min(((size_t) (last - buf)), slen);

                    buf = ngx_cpymem(buf, p, len);

                }

                fmt++;

                continue;

            case 'O':

                i64 = (int64_t) va_arg(args, off_t);

                sign = 1;

                break;

            case 'P':

                i64 = (int64_t) va_arg(args, ngx_pid_t);

                sign = 1;

                break;

            case 'T':

                i64 = (int64_t) va_arg(args, time_t);

                sign = 1;

                break;

            case 'M':

                ms = (ngx_msec_t) va_arg(args, ngx_msec_t);

                if ((ngx_msec_int_t) ms == -1) {

                    sign = 1;

                    i64 = -1;

                } else {

                    sign = 0;

                    ui64 = (uint64_t) ms;

                }

                break;

            case 'z':

                if (sign) {

                    i64 = (int64_t) va_arg(args, ssize_t);

                } else {

                    ui64 = (uint64_t) va_arg(args, size_t);

                }

                break;

            case 'i':

                if (sign) {

                    i64 = (int64_t) va_arg(args, ngx_int_t);

                } else {

                    ui64 = (uint64_t) va_arg(args, ngx_uint_t);

                }

                if (max_width) {

                    width = NGX_INT_T_LEN;

                }

                break;

            case 'd':

                if (sign) {

                    i64 = (int64_t) va_arg(args, int);

                } else {

                    ui64 = (uint64_t) va_arg(args, u_int);

                }

                break;

            case 'l':

                if (sign) {

                    i64 = (int64_t) va_arg(args, long);

                } else {

                    ui64 = (uint64_t) va_arg(args, u_long);

                }

                break;

            case 'D':

                if (sign) {

                    i64 = (int64_t) va_arg(args, int32_t);

                } else {

                    ui64 = (uint64_t) va_arg(args, uint32_t);

                }

                break;

            case 'L':

                if (sign) {

                    i64 = va_arg(args, int64_t);

                } else {

                    ui64 = va_arg(args, uint64_t);

                }

                break;

            case 'A':

                if (sign) {

                    i64 = (int64_t) va_arg(args, ngx_atomic_int_t);

                } else {

                    ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);

                }

                if (max_width) {

                    width = NGX_ATOMIC_T_LEN;

                }

                break;

            case 'f':

                f = va_arg(args, double);

                if (f < 0) {

                    *buf++ = '-';

                    f = -f;

                }

                ui64 = (int64_t) f;

                frac = 0;

                if (frac_width) {

                    scale = 1;

                    for (n = frac_width; n; n--) {

                        scale *= 10;

                    }

                    frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);

                    if (frac == scale) {

                        ui64++;

                        frac = 0;

                    }

                }

                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);

                if (frac_width) {

                    if (buf < last) {

                        *buf++ = '.';

                    }

                    buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);

                }

                fmt++;

                continue;

#if !(NGX_WIN32)

            case 'r':

                i64 = (int64_t) va_arg(args, rlim_t);

                sign = 1;

                break;

#endif

            case 'p':

                ui64 = (uintptr_t) va_arg(args, void *);

                hex = 2;

                sign = 0;

                zero = '0';

                width = NGX_PTR_SIZE * 2;

                break;

            case 'c':

                d = va_arg(args, int);

                *buf++ = (u_char) (d & 0xff);

                fmt++;

                continue;

            case 'Z':

                *buf++ = '\0';

                fmt++;

                continue;

            case 'N':

#if (NGX_WIN32)

                *buf++ = CR;

#endif

                *buf++ = LF;

                fmt++;

                continue;

            case '%':

                *buf++ = '%';

                fmt++;

                continue;

            default:

                *buf++ = *fmt++;

                continue;

            }

            if (sign) {

                if (i64 < 0) {

                    *buf++ = '-';

                    ui64 = (uint64_t) -i64;

                } else {

                    ui64 = (uint64_t) i64;

                }

            }

            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

            fmt++;

        } else {

            *buf++ = *fmt++;

        }

    }

    return buf;

}

static u_char *

‌ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,

    ngx_uint_t hexadecimal, ngx_uint_t width)

{

    u_char         *p, temp[NGX_INT64_LEN + 1];

                       /*

                        * we need temp[NGX_INT64_LEN] only,

                        * but icc issues the warning

                        */

    size_t          len;

    uint32_t        ui32;

    static u_char   hex[] = "0123456789abcdef";

    static u_char   HEX[] = "0123456789ABCDEF";

    p = temp + NGX_INT64_LEN;

    if (hexadecimal == 0) {

        if (ui64 <= (uint64_t) NGX_MAX_UINT32_VALUE) {

            /*

             * To divide 64-bit numbers and to find remainders

             * on the x86 platform gcc and icc call the libc functions

             * [u]divdi3() and [u]moddi3(), they call another function

             * in its turn.  On FreeBSD it is the qdivrem() function,

             * its source code is about 170 lines of the code.

             * The glibc counterpart is about 150 lines of the code.

             *

             * For 32-bit numbers and some divisors gcc and icc use

             * a inlined multiplication and shifts.  For example,

             * unsigned "i32 / 10" is compiled to

             *

             *     (i32 * 0xCCCCCCCD) >> 35

             */

            ui32 = (uint32_t) ui64;

            do {

                *--p = (u_char) (ui32 % 10 + '0');

            } while (ui32 /= 10);

        } else {

            do {

                *--p = (u_char) (ui64 % 10 + '0');

            } while (ui64 /= 10);

        }

    } else if (hexadecimal == 1) {

        do {

            /* the "(uint32_t)" cast disables the BCC's warning */

            *--p = hex[(uint32_t) (ui64 & 0xf)];

        } while (ui64 >>= 4);

    } else { /* hexadecimal == 2 */

        do {

            /* the "(uint32_t)" cast disables the BCC's warning */

            *--p = HEX[(uint32_t) (ui64 & 0xf)];

        } while (ui64 >>= 4);

    }

    /* zero or space padding */

    len = (temp + NGX_INT64_LEN) - p;

    while (len++ < width && buf < last) {

        *buf++ = zero;

    }

    /* number safe copy */

    len = (temp + NGX_INT64_LEN) - p;

    if (buf + len > last) {

        len = last - buf;

    }

    return ngx_cpymem(buf, p, len);

}

/*

* We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,

* and implement our own ngx_strcasecmp()/ngx_strncasecmp()

* to avoid libc locale overhead.  Besides, we use the ngx_uint_t's

* instead of the u_char's, because they are slightly faster.

*/

ngx_int_t

‌ngx_strcasecmp(u_char *s1, u_char *s2)

{

    ngx_uint_t  c1, c2;

    for ( ;; ) {

        c1 = (ngx_uint_t) *s1++;

        c2 = (ngx_uint_t) *s2++;

        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;

        if (c1 == c2) {

            if (c1) {

                continue;

            }

            return 0;

        }

        return c1 - c2;

    }

}

ngx_int_t

‌ngx_strncasecmp(u_char *s1, u_char *s2, size_t n)

{

    ngx_uint_t  c1, c2;

    while (n) {

        c1 = (ngx_uint_t) *s1++;

        c2 = (ngx_uint_t) *s2++;

        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;

        if (c1 == c2) {

            if (c1) {

                n--;

                continue;

            }

            return 0;

        }

        return c1 - c2;

    }

    return 0;

}

u_char *

‌ngx_strnstr(u_char *s1, char *s2, size_t len)

{

    u_char  c1, c2;

    size_t  n;

    c2 = *(u_char *) s2++;

    n = ngx_strlen(s2);

    do {

        do {

            if (len-- == 0) {

                return NULL;

            }

            c1 = *s1++;

            if (c1 == 0) {

                return NULL;

            }

        } while (c1 != c2);

        if (n > len) {

            return NULL;

        }

    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);

    return --s1;

}

/*

* ngx_strstrn() and ngx_strcasestrn() are intended to search for static

* substring with known length in null-terminated string. The argument n

* must be length of the second substring - 1.

*/

u_char *

‌ngx_strstrn(u_char *s1, char *s2, size_t n)

{

    u_char  c1, c2;

    c2 = *(u_char *) s2++;

    do {

        do {

            c1 = *s1++;

            if (c1 == 0) {

                return NULL;

            }

        } while (c1 != c2);

    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);

    return --s1;

}

u_char *

‌ngx_strcasestrn(u_char *s1, char *s2, size_t n)

{

    ngx_uint_t  c1, c2;

    c2 = (ngx_uint_t) *s2++;

    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;

    do {

        do {

            c1 = (ngx_uint_t) *s1++;

            if (c1 == 0) {

                return NULL;

            }

            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        } while (c1 != c2);

    } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0);

    return --s1;

}

/*

* ngx_strlcasestrn() is intended to search for static substring

* with known length in string until the argument last. The argument n

* must be length of the second substring - 1.

*/

u_char *

‌ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n)

{

    ngx_uint_t  c1, c2;

    c2 = (ngx_uint_t) *s2++;

    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;

    last -= n;

    do {

        do {

            if (s1 >= last) {

                return NULL;

            }

            c1 = (ngx_uint_t) *s1++;

            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        } while (c1 != c2);

    } while (ngx_strncasecmp(s1, s2, n) != 0);

    return --s1;

}

ngx_int_t

‌ngx_rstrncmp(u_char *s1, u_char *s2, size_t n)

{

    if (n == 0) {

        return 0;

    }

    n--;

    for ( ;; ) {

        if (s1[n] != s2[n]) {

            return s1[n] - s2[n];

        }

        if (n == 0) {

            return 0;

        }

        n--;

    }

}

ngx_int_t

‌ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n)

{

    u_char  c1, c2;

    if (n == 0) {

        return 0;

    }

    n--;

    for ( ;; ) {

        c1 = s1[n];

        if (c1 >= 'a' && c1 <= 'z') {

            c1 -= 'a' - 'A';

        }

        c2 = s2[n];

        if (c2 >= 'a' && c2 <= 'z') {

            c2 -= 'a' - 'A';

        }

        if (c1 != c2) {

            return c1 - c2;

        }

        if (n == 0) {

            return 0;

        }

        n--;

    }

}

ngx_int_t

‌ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2)

{

    size_t     n;

    ngx_int_t  m, z;

    if (n1 <= n2) {

        n = n1;

        z = -1;

    } else {

        n = n2;

        z = 1;

    }

    m = ngx_memcmp(s1, s2, n);

    if (m || n1 == n2) {

        return m;

    }

    return z;

}

ngx_int_t

‌ngx_dns_strcmp(u_char *s1, u_char *s2)

{

    ngx_uint_t  c1, c2;

    for ( ;; ) {

        c1 = (ngx_uint_t) *s1++;

        c2 = (ngx_uint_t) *s2++;

        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;

        if (c1 == c2) {

            if (c1) {

                continue;

            }

            return 0;

        }

        /* in ASCII '.' > '-', but we need '.' to be the lowest character */

        c1 = (c1 == '.') ? ' ' : c1;

        c2 = (c2 == '.') ? ' ' : c2;

        return c1 - c2;

    }

}

ngx_int_t

‌ngx_filename_cmp(u_char *s1, u_char *s2, size_t n)

{

    ngx_uint_t  c1, c2;

    while (n) {

        c1 = (ngx_uint_t) *s1++;

        c2 = (ngx_uint_t) *s2++;

#if (NGX_HAVE_CASELESS_FILESYSTEM)

        c1 = tolower(c1);

        c2 = tolower(c2);

#endif

        if (c1 == c2) {

            if (c1) {

                n--;

                continue;

            }

            return 0;

        }

        /* we need '/' to be the lowest character */

        if (c1 == 0 || c2 == 0) {

            return c1 - c2;

        }

        c1 = (c1 == '/') ? 0 : c1;

        c2 = (c2 == '/') ? 0 : c2;

        return c1 - c2;

    }

    return 0;

}

ngx_int_t

‌ngx_atoi(u_char *line, size_t n)

{

    ngx_int_t  value;

    if (n == 0) {

        return NGX_ERROR;

    }

    for (value = 0; n--; line++) {

        if (*line < '0' || *line > '9') {

            return NGX_ERROR;

        }

        value = value * 10 + (*line - '0');

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

/* parse a fixed point number, e.g., ngx_atofp("10.5", 4, 2) returns 1050 */

ngx_int_t

‌ngx_atofp(u_char *line, size_t n, size_t point)

{

    ngx_int_t   value;

    ngx_uint_t  dot;

    if (n == 0) {

        return NGX_ERROR;

    }

    dot = 0;

    for (value = 0; n--; line++) {

        if (point == 0) {

            return NGX_ERROR;

        }

        if (*line == '.') {

            if (dot) {

                return NGX_ERROR;

            }

            dot = 1;

            continue;

        }

        if (*line < '0' || *line > '9') {

            return NGX_ERROR;

        }

        value = value * 10 + (*line - '0');

        point -= dot;

    }

    while (point--) {

        value = value * 10;

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

ssize_t

‌ngx_atosz(u_char *line, size_t n)

{

    ssize_t  value;

    if (n == 0) {

        return NGX_ERROR;

    }

    for (value = 0; n--; line++) {

        if (*line < '0' || *line > '9') {

            return NGX_ERROR;

        }

        value = value * 10 + (*line - '0');

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

off_t

‌ngx_atoof(u_char *line, size_t n)

{

    off_t  value;

    if (n == 0) {

        return NGX_ERROR;

    }

    for (value = 0; n--; line++) {

        if (*line < '0' || *line > '9') {

            return NGX_ERROR;

        }

        value = value * 10 + (*line - '0');

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

time_t

‌ngx_atotm(u_char *line, size_t n)

{

    time_t  value;

    if (n == 0) {

        return NGX_ERROR;

    }

    for (value = 0; n--; line++) {

        if (*line < '0' || *line > '9') {

            return NGX_ERROR;

        }

        value = value * 10 + (*line - '0');

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

ngx_int_t

‌ngx_hextoi(u_char *line, size_t n)

{

    u_char     c, ch;

    ngx_int_t  value;

    if (n == 0) {

        return NGX_ERROR;

    }

    for (value = 0; n--; line++) {

        ch = *line;

        if (ch >= '0' && ch <= '9') {

            value = value * 16 + (ch - '0');

            continue;

        }

        c = (u_char) (ch | 0x20);

        if (c >= 'a' && c <= 'f') {

            value = value * 16 + (c - 'a' + 10);

            continue;

        }

        return NGX_ERROR;

    }

    if (value < 0) {

        return NGX_ERROR;

    } else {

        return value;

    }

}

u_char *

‌ngx_hex_dump(u_char *dst, u_char *src, size_t len)

{

    static u_char  hex[] = "0123456789abcdef";

    while (len--) {

        *dst++ = hex[*src >> 4];

        *dst++ = hex[*src++ & 0xf];

    }

    return dst;

}

void

‌ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src)

{

    static u_char   basis64[] =

            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    ngx_encode_base64_internal(dst, src, basis64, 1);

}

void

‌ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src)

{

    static u_char   basis64[] =

            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

    ngx_encode_base64_internal(dst, src, basis64, 0);

}

static void

‌ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis,

    ngx_uint_t padding)

{

    u_char         *d, *s;

    size_t          len;

    len = src->len;

    s = src->data;

    d = dst->data;

    while (len > 2) {

        *d++ = basis[(s[0] >> 2) & 0x3f];

        *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];

        *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)];

        *d++ = basis[s[2] & 0x3f];

        s += 3;

        len -= 3;

    }

    if (len) {

        *d++ = basis[(s[0] >> 2) & 0x3f];

        if (len == 1) {

            *d++ = basis[(s[0] & 3) << 4];

            if (padding) {

                *d++ = '=';

            }

        } else {

            *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];

            *d++ = basis[(s[1] & 0x0f) << 2];

        }

        if (padding) {

            *d++ = '=';

        }

    }

    dst->len = d - dst->data;

}

ngx_int_t

‌ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src)

{

    static u_char   basis64[] = {

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,

        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,

        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,

        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,

        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,

        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77

    };

    return ngx_decode_base64_internal(dst, src, basis64);

}

ngx_int_t

‌ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src)

{

    static u_char   basis64[] = {

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77,

        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,

        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,

        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63,

        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,

        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77

    };

    return ngx_decode_base64_internal(dst, src, basis64);

}

static ngx_int_t

‌ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)

{

    size_t          len;

    u_char         *d, *s;

    for (len = 0; len < src->len; len++) {

        if (src->data[len] == '=') {

            break;

        }

        if (basis[src->data[len]] == 77) {

            return NGX_ERROR;

        }

    }

    if (len % 4 == 1) {

        return NGX_ERROR;

    }

    s = src->data;

    d = dst->data;

    while (len > 3) {

        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);

        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);

        *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);

        s += 4;

        len -= 4;

    }

    if (len > 1) {

        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);

    }

    if (len > 2) {

        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);

    }

    dst->len = d - dst->data;

    return NGX_OK;

}

/*

* ngx_utf8_decode() decodes two and more bytes UTF sequences only

* the return values:

*    0x80 - 0x10ffff         valid character

*    0x110000 - 0xfffffffd   invalid sequence

*    0xfffffffe              incomplete sequence

*    0xffffffff              error

*/

uint32_t

‌ngx_utf8_decode(u_char **p, size_t n)

{

    size_t    len;

    uint32_t  u, i, valid;

    u = **p;

    if (u >= 0xf0) {

        u &= 0x07;

        valid = 0xffff;

        len = 3;

    } else if (u >= 0xe0) {

        u &= 0x0f;

        valid = 0x7ff;

        len = 2;

    } else if (u >= 0xc2) {

        u &= 0x1f;

        valid = 0x7f;

        len = 1;

    } else {

        (*p)++;

        return 0xffffffff;

    }

    if (n - 1 < len) {

        return 0xfffffffe;

    }

    (*p)++;

    while (len) {

        i = *(*p)++;

        if (i < 0x80) {

            return 0xffffffff;

        }

        u = (u << 6) | (i & 0x3f);

        len--;

    }

    if (u > valid) {

        return u;

    }

    return 0xffffffff;

}

size_t

‌ngx_utf8_length(u_char *p, size_t n)

{

    u_char  c, *last;

    size_t  len;

    last = p + n;

    for (len = 0; p < last; len++) {

        c = *p;

        if (c < 0x80) {

            p++;

            continue;

        }

        if (ngx_utf8_decode(&p, n) > 0x10ffff) {

            /* invalid UTF-8 */

            return n;

        }

    }

    return len;

}

u_char *

‌ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)

{

    u_char  c, *next;

    if (n == 0) {

        return dst;

    }

    while (--n) {

        c = *src;

        *dst = c;

        if (c < 0x80) {

            if (c != '\0') {

                dst++;

                src++;

                len--;

                continue;

            }

            return dst;

        }

        next = src;

        if (ngx_utf8_decode(&next, len) > 0x10ffff) {

            /* invalid UTF-8 */

            break;

        }

        while (src < next) {

            *dst++ = *src++;

            len--;

        }

    }

    *dst = '\0';

    return dst;

}

uintptr_t

‌ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)

{

    ngx_uint_t      n;

    uint32_t       *escape;

    static u_char   hex[] = "0123456789ABCDEF";

                    /* " ", "#", "%", "?", %00-%1F, %7F-%FF */

    static uint32_t   uri[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x80000029, /* 1000 0000 0000 0000  0000 0000 0010 1001 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

                    /* " ", "#", "%", "&", "+", "?", %00-%1F, %7F-%FF */

    static uint32_t   args[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x88000869, /* 1000 1000 0000 0000  0000 1000 0110 1001 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

                    /* not ALPHA, DIGIT, "-", ".", "_", "~" */

    static uint32_t   uri_component[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0xfc009fff, /* 1111 1100 0000 0000  1001 1111 1111 1111 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x78000001, /* 0111 1000 0000 0000  0000 0000 0000 0001 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

                    /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */

    static uint32_t   html[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x000000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

                    /* " ", """, "%", "'", %00-%1F, %7F-%FF */

    static uint32_t   refresh[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

                    /* " ", "%", %00-%1F */

    static uint32_t   memcached[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

    };

                    /* mail_auth is the same as memcached */

    static uint32_t  *map[] =

        { uri, args, uri_component, html, refresh, memcached, memcached };

    escape = map[type];

    if (dst == NULL) {

        /* find the number of the characters to be escaped */

        n = 0;

        while (size) {

            if (escape[*src >> 5] & (1 << (*src & 0x1f))) {

                n++;

            }

            src++;

            size--;

        }

        return (uintptr_t) n;

    }

    while (size) {

        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {

            *dst++ = '%';

            *dst++ = hex[*src >> 4];

            *dst++ = hex[*src & 0xf];

            src++;

        } else {

            *dst++ = *src++;

        }

        size--;

    }

    return (uintptr_t) dst;

}

void

‌ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)

{

    u_char  *d, *s, ch, c, decoded;

    enum {

        sw_usual = 0,

        sw_quoted,

        sw_quoted_second

    } state;

    d = *dst;

    s = *src;

    state = 0;

    decoded = 0;

    while (size--) {

        ch = *s++;

        switch (state) {

        case sw_usual:

            if (ch == '?'

                && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))

            {

                *d++ = ch;

                goto done;

            }

            if (ch == '%') {

                state = sw_quoted;

                break;

            }

            *d++ = ch;

            break;

        case sw_quoted:

            if (ch >= '0' && ch <= '9') {

                decoded = (u_char) (ch - '0');

                state = sw_quoted_second;

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                decoded = (u_char) (c - 'a' + 10);

                state = sw_quoted_second;

                break;

            }

            /* the invalid quoted character */

            state = sw_usual;

            *d++ = ch;

            break;

        case sw_quoted_second:

            state = sw_usual;

            if (ch >= '0' && ch <= '9') {

                ch = (u_char) ((decoded << 4) + ch - '0');

                if (type & NGX_UNESCAPE_REDIRECT) {

                    if (ch > '%' && ch < 0x7f) {

                        *d++ = ch;

                        break;

                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);

                    break;

                }

                *d++ = ch;

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                ch = (u_char) ((decoded << 4) + c - 'a' + 10);

                if (type & NGX_UNESCAPE_URI) {

                    if (ch == '?') {

                        *d++ = ch;

                        goto done;

                    }

                    *d++ = ch;

                    break;

                }

                if (type & NGX_UNESCAPE_REDIRECT) {

                    if (ch == '?') {

                        *d++ = ch;

                        goto done;

                    }

                    if (ch > '%' && ch < 0x7f) {

                        *d++ = ch;

                        break;

                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);

                    break;

                }

                *d++ = ch;

                break;

            }

            /* the invalid quoted character */

            break;

        }

    }

done:

    *dst = d;

    *src = s;

}

uintptr_t

‌ngx_escape_html(u_char *dst, u_char *src, size_t size)

{

    u_char      ch;

    ngx_uint_t  len;

    if (dst == NULL) {

        len = 0;

        while (size) {

            switch (*src++) {

            case '<':

                len += sizeof("&lt;") - 2;

                break;

            case '>':

                len += sizeof("&gt;") - 2;

                break;

            case '&':

                len += sizeof("&amp;") - 2;

                break;

            case '"':

                len += sizeof("&quot;") - 2;

                break;

            default:

                break;

            }

            size--;

        }

        return (uintptr_t) len;

    }

    while (size) {

        ch = *src++;

        switch (ch) {

        case '<':

            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';

            break;

        case '>':

            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';

            break;

        case '&':

            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';

            *dst++ = ';';

            break;

        case '"':

            *dst++ = '&'; *dst++ = 'q'; *dst++ = 'u'; *dst++ = 'o';

            *dst++ = 't'; *dst++ = ';';

            break;

        default:

            *dst++ = ch;

            break;

        }

        size--;

    }

    return (uintptr_t) dst;

}

uintptr_t

‌ngx_escape_json(u_char *dst, u_char *src, size_t size)

{

    u_char      ch;

    ngx_uint_t  len;

    if (dst == NULL) {

        len = 0;

        while (size) {

            ch = *src++;

            if (ch == '\\' || ch == '"') {

                len++;

            } else if (ch <= 0x1f) {

                len += sizeof("\\u001F") - 2;

            }

            size--;

        }

        return (uintptr_t) len;

    }

    while (size) {

        ch = *src++;

        if (ch > 0x1f) {

            if (ch == '\\' || ch == '"') {

                *dst++ = '\\';

            }

            *dst++ = ch;

        } else {

            *dst++ = '\\'; *dst++ = 'u'; *dst++ = '0'; *dst++ = '0';

            *dst++ = '0' + (ch >> 4);

            ch &= 0xf;

            *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10);

        }

        size--;

    }

    return (uintptr_t) dst;

}

void

‌ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    ngx_str_node_t      *n, *t;

    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        n = (ngx_str_node_t *) node;

        t = (ngx_str_node_t *) temp;

        if (node->key != temp->key) {

            p = (node->key < temp->key) ? &temp->left : &temp->right;

        } else if (n->str.len != t->str.len) {

            p = (n->str.len < t->str.len) ? &temp->left : &temp->right;

        } else {

            p = (ngx_memcmp(n->str.data, t->str.data, n->str.len) < 0)

                 ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

ngx_str_node_t *

‌ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash)

{

    ngx_int_t           rc;

    ngx_str_node_t     *n;

    ngx_rbtree_node_t  *node, *sentinel;

    node = rbtree->root;

    sentinel = rbtree->sentinel;

    while (node != sentinel) {

        n = (ngx_str_node_t *) node;

        if (hash != node->key) {

            node = (hash < node->key) ? node->left : node->right;

            continue;

        }

        if (val->len != n->str.len) {

            node = (val->len < n->str.len) ? node->left : node->right;

            continue;

        }

        rc = ngx_memcmp(val->data, n->str.data, val->len);

        if (rc < 0) {

            node = node->left;

            continue;

        }

        if (rc > 0) {

            node = node->right;

            continue;

        }

        return n;

    }

    return NULL;

}

/* ngx_sort() is implemented as insertion sort because we need stable sort */

void

‌ngx_sort(void *base, size_t n, size_t size,

    ngx_int_t (*cmp)(const void *, const void *))

{

    u_char  *p1, *p2, *p;

    p = ngx_alloc(size, ngx_cycle->log);

    if (p == NULL) {

        return;

    }

    for (p1 = (u_char *) base + size;

         p1 < (u_char *) base + n * size;

         p1 += size)

    {

        ngx_memcpy(p, p1, size);

        for (p2 = p1;

             p2 > (u_char *) base && cmp(p2 - size, p) > 0;

             p2 -= size)

        {

            ngx_memcpy(p2, p2 - size, size);

        }

        ngx_memcpy(p2, p, size);

    }

    ngx_free(p);

}

#if (NGX_MEMCPY_LIMIT)

void *

‌ngx_memcpy(void *dst, const void *src, size_t n)

{

    if (n > NGX_MEMCPY_LIMIT) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "memcpy %uz bytes", n);

        ngx_debug_point();

    }

    return memcpy(dst, src, n);

}

#endif

src/core/ngx_palloc.c - nginx-1.7.9

Functions defined

ngx_create_pool

ngx_destroy_pool

ngx_palloc

ngx_palloc_block

ngx_palloc_large

ngx_pcalloc

ngx_pfree

ngx_pmemalign

ngx_pnalloc

ngx_pool_cleanup_add

ngx_pool_cleanup_file

ngx_pool_delete_file

ngx_pool_run_cleanup_file

ngx_reset_pool

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);

ngx_pool_t *

‌ngx_create_pool(size_t size, ngx_log_t *log)

{

    ngx_pool_t  *p;

    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);

    if (p == NULL) {

        return NULL;

    }

    p->d.last = (u_char *) p + sizeof(ngx_pool_t);

    p->d.end = (u_char *) p + size;

    p->d.next = NULL;

    p->d.failed = 0;

    size = size - sizeof(ngx_pool_t);

    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    p->current = p;

    p->chain = NULL;

    p->large = NULL;

    p->cleanup = NULL;

    p->log = log;

    return p;

}

void

‌ngx_destroy_pool(ngx_pool_t *pool)

{

    ngx_pool_t          *p, *n;

    ngx_pool_large_t    *l;

    ngx_pool_cleanup_t  *c;

    for (c = pool->cleanup; c; c = c->next) {

        if (c->handler) {

            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

                           "run cleanup: %p", c);

            c->handler(c->data);

        }

    }

    for (l = pool->large; l; l = l->next) {

        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);

        if (l->alloc) {

            ngx_free(l->alloc);

        }

    }

#if (NGX_DEBUG)

    /*

     * we could allocate the pool->log from this pool

     * so we cannot use this log while free()ing the pool

     */

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {

        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

                       "free: %p, unused: %uz", p, p->d.end - p->d.last);

        if (n == NULL) {

            break;

        }

    }

#endif

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {

        ngx_free(p);

        if (n == NULL) {

            break;

        }

    }

}

void

‌ngx_reset_pool(ngx_pool_t *pool)

{

    ngx_pool_t        *p;

    ngx_pool_large_t  *l;

    for (l = pool->large; l; l = l->next) {

        if (l->alloc) {

            ngx_free(l->alloc);

        }

    }

    for (p = pool; p; p = p->d.next) {

        p->d.last = (u_char *) p + sizeof(ngx_pool_t);

        p->d.failed = 0;

    }

    pool->current = pool;

    pool->chain = NULL;

    pool->large = NULL;

}

void *

‌ngx_palloc(ngx_pool_t *pool, size_t size)

{

    u_char      *m;

    ngx_pool_t  *p;

    if (size <= pool->max) {

        p = pool->current;

        do {

            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);

            if ((size_t) (p->d.end - m) >= size) {

                p->d.last = m + size;

                return m;

            }

            p = p->d.next;

        } while (p);

        return ngx_palloc_block(pool, size);

    }

    return ngx_palloc_large(pool, size);

}

void *

‌ngx_pnalloc(ngx_pool_t *pool, size_t size)

{

    u_char      *m;

    ngx_pool_t  *p;

    if (size <= pool->max) {

        p = pool->current;

        do {

            m = p->d.last;

            if ((size_t) (p->d.end - m) >= size) {

                p->d.last = m + size;

                return m;

            }

            p = p->d.next;

        } while (p);

        return ngx_palloc_block(pool, size);

    }

    return ngx_palloc_large(pool, size);

}

static void *

‌ngx_palloc_block(ngx_pool_t *pool, size_t size)

{

    u_char      *m;

    size_t       psize;

    ngx_pool_t  *p, *new;

    psize = (size_t) (pool->d.end - (u_char *) pool);

    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);

    if (m == NULL) {

        return NULL;

    }

    new = (ngx_pool_t *) m;

    new->d.end = m + psize;

    new->d.next = NULL;

    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);

    m = ngx_align_ptr(m, NGX_ALIGNMENT);

    new->d.last = m + size;

    for (p = pool->current; p->d.next; p = p->d.next) {

        if (p->d.failed++ > 4) {

            pool->current = p->d.next;

        }

    }

    p->d.next = new;

    return m;

}

static void *

‌ngx_palloc_large(ngx_pool_t *pool, size_t size)

{

    void              *p;

    ngx_uint_t         n;

    ngx_pool_large_t  *large;

    p = ngx_alloc(size, pool->log);

    if (p == NULL) {

        return NULL;

    }

    n = 0;

    for (large = pool->large; large; large = large->next) {

        if (large->alloc == NULL) {

            large->alloc = p;

            return p;

        }

        if (n++ > 3) {

            break;

        }

    }

    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));

    if (large == NULL) {

        ngx_free(p);

        return NULL;

    }

    large->alloc = p;

    large->next = pool->large;

    pool->large = large;

    return p;

}

void *

‌ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)

{

    void              *p;

    ngx_pool_large_t  *large;

    p = ngx_memalign(alignment, size, pool->log);

    if (p == NULL) {

        return NULL;

    }

    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));

    if (large == NULL) {

        ngx_free(p);

        return NULL;

    }

    large->alloc = p;

    large->next = pool->large;

    pool->large = large;

    return p;

}

ngx_int_t

‌ngx_pfree(ngx_pool_t *pool, void *p)

{

    ngx_pool_large_t  *l;

    for (l = pool->large; l; l = l->next) {

        if (p == l->alloc) {

            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

                           "free: %p", l->alloc);

            ngx_free(l->alloc);

            l->alloc = NULL;

            return NGX_OK;

        }

    }

    return NGX_DECLINED;

}

void *

‌ngx_pcalloc(ngx_pool_t *pool, size_t size)

{

    void *p;

    p = ngx_palloc(pool, size);

    if (p) {

        ngx_memzero(p, size);

    }

    return p;

}

ngx_pool_cleanup_t *

‌ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)

{

    ngx_pool_cleanup_t  *c;

    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));

    if (c == NULL) {

        return NULL;

    }

    if (size) {

        c->data = ngx_palloc(p, size);

        if (c->data == NULL) {

            return NULL;

        }

    } else {

        c->data = NULL;

    }

    c->handler = NULL;

    c->next = p->cleanup;

    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;

}

void

‌ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)

{

    ngx_pool_cleanup_t       *c;

    ngx_pool_cleanup_file_t  *cf;

    for (c = p->cleanup; c; c = c->next) {

        if (c->handler == ngx_pool_cleanup_file) {

            cf = c->data;

            if (cf->fd == fd) {

                c->handler(cf);

                c->handler = NULL;

                return;

            }

        }

    }

}

void

‌ngx_pool_cleanup_file(void *data)

{

    ngx_pool_cleanup_file_t  *c = data;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",

                   c->fd);

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", c->name);

    }

}

void

‌ngx_pool_delete_file(void *data)

{

    ngx_pool_cleanup_file_t  *c = data;

    ngx_err_t  err;

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",

                   c->fd, c->name);

    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {

        err = ngx_errno;

        if (err != NGX_ENOENT) {

            ngx_log_error(NGX_LOG_CRIT, c->log, err,

                          ngx_delete_file_n " \"%s\" failed", c->name);

        }

    }

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", c->name);

    }

}

#if 0

static void *

ngx_get_cached_block(size_t size)

{

    void                     *p;

    ngx_cached_block_slot_t  *slot;

    if (ngx_cycle->cache == NULL) {

        return NULL;

    }

    slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];

    slot->tries++;

    if (slot->number) {

        p = slot->block;

        slot->block = slot->block->next;

        slot->number--;

        return p;

    }

    return NULL;

}

#endif

src/os/unix/ngx_alloc.c - nginx-1.7.9

Global variables defined

ngx_cacheline_size

ngx_pagesize

ngx_pagesize_shift

Functions defined

ngx_alloc

ngx_calloc

ngx_memalign

ngx_memalign

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌ngx_uint_t  ngx_pagesize;

‌ngx_uint_t  ngx_pagesize_shift;

‌ngx_uint_t  ngx_cacheline_size;

void *

‌ngx_alloc(size_t size, ngx_log_t *log)

{

    void  *p;

    p = malloc(size);

    if (p == NULL) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                      "malloc(%uz) failed", size);

    }

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);

    return p;

}

void *

‌ngx_calloc(size_t size, ngx_log_t *log)

{

    void  *p;

    p = ngx_alloc(size, log);

    if (p) {

        ngx_memzero(p, size);

    }

    return p;

}

#if (NGX_HAVE_POSIX_MEMALIGN)

void *

‌ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)

{

    void  *p;

    int    err;

    err = posix_memalign(&p, alignment, size);

    if (err) {

        ngx_log_error(NGX_LOG_EMERG, log, err,

                      "posix_memalign(%uz, %uz) failed", alignment, size);

        p = NULL;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,

                   "posix_memalign: %p:%uz @%uz", p, size, alignment);

    return p;

}

#elif (NGX_HAVE_MEMALIGN)

void *

‌ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)

{

    void  *p;

    p = memalign(alignment, size);

    if (p == NULL) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                      "memalign(%uz, %uz) failed", alignment, size);

    }

    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,

                   "memalign: %p:%uz @%uz", p, size, alignment);

    return p;

}

#endif

src/os/unix/ngx_errno.h - nginx-1.7.9

Data types defined

ngx_err_t

Macros defined

NGX_EACCES

NGX_EADDRINUSE

NGX_EAGAIN

NGX_EAGAIN

NGX_EBADF

NGX_EBUSY

NGX_ECANCELED

NGX_ECHILD

NGX_ECONNABORTED

NGX_ECONNREFUSED

NGX_ECONNRESET

NGX_EEXIST

NGX_EHOSTDOWN

NGX_EHOSTUNREACH

NGX_EILSEQ

NGX_EINPROGRESS

NGX_EINTR

NGX_EINVAL

NGX_EISDIR

NGX_ELOOP

NGX_EMFILE

NGX_EMLINK

NGX_ENAMETOOLONG

NGX_ENETDOWN

NGX_ENETUNREACH

NGX_ENFILE

NGX_ENOENT

NGX_ENOMEM

NGX_ENOMOREFILES

NGX_ENOPATH

NGX_ENOPROTOOPT

NGX_ENOSPC

NGX_ENOSYS

NGX_ENOTCONN

NGX_ENOTDIR

NGX_EOPNOTSUPP

NGX_EPERM

NGX_EPIPE

NGX_ESRCH

NGX_ETIMEDOUT

NGX_EXDEV

_NGX_ERRNO_H_INCLUDED_

ngx_errno

ngx_set_errno

ngx_set_socket_errno

ngx_socket_errno

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_ERRNO_H_INCLUDED_

‌#define _NGX_ERRNO_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef int               ngx_err_t;

‌#define NGX_EPERM         EPERM

‌#define NGX_ENOENT        ENOENT

‌#define NGX_ENOPATH       ENOENT

‌#define NGX_ESRCH         ESRCH

‌#define NGX_EINTR         EINTR

‌#define NGX_ECHILD        ECHILD

‌#define NGX_ENOMEM        ENOMEM

‌#define NGX_EACCES        EACCES

‌#define NGX_EBUSY         EBUSY

‌#define NGX_EEXIST        EEXIST

‌#define NGX_EXDEV         EXDEV

‌#define NGX_ENOTDIR       ENOTDIR

‌#define NGX_EISDIR        EISDIR

‌#define NGX_EINVAL        EINVAL

‌#define NGX_ENFILE        ENFILE

‌#define NGX_EMFILE        EMFILE

‌#define NGX_ENOSPC        ENOSPC

‌#define NGX_EPIPE         EPIPE

‌#define NGX_EINPROGRESS   EINPROGRESS

‌#define NGX_ENOPROTOOPT   ENOPROTOOPT

‌#define NGX_EOPNOTSUPP    EOPNOTSUPP

‌#define NGX_EADDRINUSE    EADDRINUSE

‌#define NGX_ECONNABORTED  ECONNABORTED

‌#define NGX_ECONNRESET    ECONNRESET

‌#define NGX_ENOTCONN      ENOTCONN

‌#define NGX_ETIMEDOUT     ETIMEDOUT

‌#define NGX_ECONNREFUSED  ECONNREFUSED

‌#define NGX_ENAMETOOLONG  ENAMETOOLONG

‌#define NGX_ENETDOWN      ENETDOWN

‌#define NGX_ENETUNREACH   ENETUNREACH

‌#define NGX_EHOSTDOWN     EHOSTDOWN

‌#define NGX_EHOSTUNREACH  EHOSTUNREACH

‌#define NGX_ENOSYS        ENOSYS

‌#define NGX_ECANCELED     ECANCELED

‌#define NGX_EILSEQ        EILSEQ

‌#define NGX_ENOMOREFILES  0

‌#define NGX_ELOOP         ELOOP

‌#define NGX_EBADF         EBADF

#if (NGX_HAVE_OPENAT)

‌#define NGX_EMLINK        EMLINK

#endif

#if (__hpux__)

‌#define NGX_EAGAIN        EWOULDBLOCK

#else

‌#define NGX_EAGAIN        EAGAIN

#endif

‌#define ngx_errno                  errno

‌#define ngx_socket_errno           errno

‌#define ngx_set_errno(err)         errno = err

‌#define ngx_set_socket_errno(err)  errno = err

u_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size);

ngx_int_t ngx_strerror_init(void);

#endif /* _NGX_ERRNO_H_INCLUDED_ */

src/os/unix/ngx_errno.c - nginx-1.7.9

Global variables defined

ngx_sys_errlist

ngx_unknown_error

Functions defined

ngx_strerror

ngx_strerror_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* The strerror() messages are copied because:

*

* 1) strerror() and strerror_r() functions are not Async-Signal-Safe,

*    therefore, they cannot be used in signal handlers;

*

* 2) a direct sys_errlist[] array may be used instead of these functions,

*    but Linux linker warns about its usage:

*

* warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead

* warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead

*

*    causing false bug reports.

*/

‌static ngx_str_t  *ngx_sys_errlist;

‌static ngx_str_t   ngx_unknown_error = ngx_string("Unknown error");

u_char *

‌ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)

{

    ngx_str_t  *msg;

    msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]:

                                              &ngx_unknown_error;

    size = ngx_min(size, msg->len);

    return ngx_cpymem(errstr, msg->data, size);

}

ngx_int_t

‌ngx_strerror_init(void)

{

    char       *msg;

    u_char     *p;

    size_t      len;

    ngx_err_t   err;

    /*

     * ngx_strerror() is not ready to work at this stage, therefore,

     * malloc() is used and possible errors are logged using strerror().

     */

    len = NGX_SYS_NERR * sizeof(ngx_str_t);

    ngx_sys_errlist = malloc(len);

    if (ngx_sys_errlist == NULL) {

        goto failed;

    }

    for (err = 0; err < NGX_SYS_NERR; err++) {

        msg = strerror(err);

        len = ngx_strlen(msg);

        p = malloc(len);

        if (p == NULL) {

            goto failed;

        }

        ngx_memcpy(p, msg, len);

        ngx_sys_errlist[err].len = len;

        ngx_sys_errlist[err].data = p;

    }

    return NGX_OK;

failed:

    err = errno;

    ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));

    return NGX_ERROR;

}

src/core/ngx_palloc.h - nginx-1.7.9

Data types defined

ngx_pool_cleanup_file_t

ngx_pool_cleanup_pt

ngx_pool_cleanup_s

ngx_pool_cleanup_t

ngx_pool_data_t

ngx_pool_large_s

ngx_pool_large_t

ngx_pool_s

Macros defined

NGX_DEFAULT_POOL_SIZE

NGX_MAX_ALLOC_FROM_POOL

NGX_MIN_POOL_SIZE

NGX_POOL_ALIGNMENT

_NGX_PALLOC_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_PALLOC_H_INCLUDED_

‌#define _NGX_PALLOC_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

/*

* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.

* On Windows NT it decreases a number of locked pages in a kernel.

*/

‌#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

‌#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)

‌#define NGX_POOL_ALIGNMENT       16

‌#define NGX_MIN_POOL_SIZE                                                     \

    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \

              NGX_POOL_ALIGNMENT)

‌typedef void (*ngx_pool_cleanup_pt)(void *data);

‌typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

‌struct ngx_pool_cleanup_s {

    ngx_pool_cleanup_pt   handler;

    void                 *data;

    ngx_pool_cleanup_t   *next;

};

‌typedef struct ngx_pool_large_s  ngx_pool_large_t;

‌struct ngx_pool_large_s {

    ngx_pool_large_t     *next;

    void                 *alloc;

};

typedef struct {

    u_char               *last;

    u_char               *end;

    ngx_pool_t           *next;

    ngx_uint_t            failed;

‌} ngx_pool_data_t;

‌struct ngx_pool_s {

    ngx_pool_data_t       d;

    size_t                max;

    ngx_pool_t           *current;

    ngx_chain_t          *chain;

    ngx_pool_large_t     *large;

    ngx_pool_cleanup_t   *cleanup;

    ngx_log_t            *log;

};

typedef struct {

    ngx_fd_t              fd;

    u_char               *name;

    ngx_log_t            *log;

‌} ngx_pool_cleanup_file_t;

void *ngx_alloc(size_t size, ngx_log_t *log);

void *ngx_calloc(size_t size, ngx_log_t *log);

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);

void ngx_destroy_pool(ngx_pool_t *pool);

void ngx_reset_pool(ngx_pool_t *pool);

void *ngx_palloc(ngx_pool_t *pool, size_t size);

void *ngx_pnalloc(ngx_pool_t *pool, size_t size);

void *ngx_pcalloc(ngx_pool_t *pool, size_t size);

void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);

ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);

void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);

void ngx_pool_cleanup_file(void *data);

void ngx_pool_delete_file(void *data);

#endif /* _NGX_PALLOC_H_INCLUDED_ */

src/os/unix/ngx_alloc.h - nginx-1.7.9

Macros defined

_NGX_ALLOC_H_INCLUDED_

ngx_free

ngx_memalign

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_ALLOC_H_INCLUDED_

‌#define _NGX_ALLOC_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

void *ngx_alloc(size_t size, ngx_log_t *log);

void *ngx_calloc(size_t size, ngx_log_t *log);

‌#define ngx_free          free

/*

* Linux has memalign() or posix_memalign()

* Solaris has memalign()

* FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()

* aligns allocations bigger than page size at the page boundary

*/

#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)

void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);

#else

‌#define ngx_memalign(alignment, size, log)  ngx_alloc(size, log)

#endif

extern ngx_uint_t  ngx_pagesize;

extern ngx_uint_t  ngx_pagesize_shift;

extern ngx_uint_t  ngx_cacheline_size;

#endif /* _NGX_ALLOC_H_INCLUDED_ */

src/core/ngx_times.c - nginx-1.7.9

Global variables defined

cached_err_log_time

cached_gmtoff

cached_http_log_iso8601

cached_http_log_time

cached_http_time

cached_syslog_time

cached_time

months

ngx_cached_err_log_time

ngx_cached_http_log_iso8601

ngx_cached_http_log_time

ngx_cached_http_time

ngx_cached_syslog_time

ngx_cached_time

ngx_current_msec

ngx_time_lock

slot

week

Functions defined

ngx_gmtime

ngx_http_cookie_time

ngx_http_time

ngx_next_time

ngx_time_init

ngx_time_sigsafe_update

ngx_time_update

Macros defined

NGX_TIME_SLOTS

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* The time may be updated by signal handler or by several threads.

* The time update operations are rare and require to hold the ngx_time_lock.

* The time read operations are frequent, so they are lock-free and get time

* values and strings from the current slot.  Thus thread may get the corrupted

* values only if it is preempted while copying and then it is not scheduled

* to run more than NGX_TIME_SLOTS seconds.

*/

‌#define NGX_TIME_SLOTS   64

‌static ngx_uint_t        slot;

‌static ngx_atomic_t      ngx_time_lock;

‌volatile ngx_msec_t      ngx_current_msec;

‌volatile ngx_time_t     *ngx_cached_time;

‌volatile ngx_str_t       ngx_cached_err_log_time;

‌volatile ngx_str_t       ngx_cached_http_time;

‌volatile ngx_str_t       ngx_cached_http_log_time;

‌volatile ngx_str_t       ngx_cached_http_log_iso8601;

‌volatile ngx_str_t       ngx_cached_syslog_time;

#if !(NGX_WIN32)

/*

* localtime() and localtime_r() are not Async-Signal-Safe functions, therefore,

* they must not be called by a signal handler, so we use the cached

* GMT offset value. Fortunately the value is changed only two times a year.

*/

‌static ngx_int_t         cached_gmtoff;

#endif

‌static ngx_time_t        cached_time[NGX_TIME_SLOTS];

‌static u_char            cached_err_log_time[NGX_TIME_SLOTS]

                                    [sizeof("1970/09/28 12:00:00")];

‌static u_char            cached_http_time[NGX_TIME_SLOTS]

                                    [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];

‌static u_char            cached_http_log_time[NGX_TIME_SLOTS]

                                    [sizeof("28/Sep/1970:12:00:00 +0600")];

‌static u_char            cached_http_log_iso8601[NGX_TIME_SLOTS]

                                    [sizeof("1970-09-28T12:00:00+06:00")];

‌static u_char            cached_syslog_time[NGX_TIME_SLOTS]

                                    [sizeof("Sep 28 12:00:00")];

‌static char  *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

‌static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",

                           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

void

‌ngx_time_init(void)

{

    ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;

    ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;

    ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;

    ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;

    ngx_cached_syslog_time.len = sizeof("Sep 28 12:00:00") - 1;

    ngx_cached_time = &cached_time[0];

    ngx_time_update();

}

void

‌ngx_time_update(void)

{

    u_char          *p0, *p1, *p2, *p3, *p4;

    ngx_tm_t         tm, gmt;

    time_t           sec;

    ngx_uint_t       msec;

    ngx_time_t      *tp;

    struct timeval   tv;

    if (!ngx_trylock(&ngx_time_lock)) {

        return;

    }

    ngx_gettimeofday(&tv);

    sec = tv.tv_sec;

    msec = tv.tv_usec / 1000;

    ngx_current_msec = (ngx_msec_t) sec * 1000 + msec;

    tp = &cached_time[slot];

    if (tp->sec == sec) {

        tp->msec = msec;

        ngx_unlock(&ngx_time_lock);

        return;

    }

    if (slot == NGX_TIME_SLOTS - 1) {

        slot = 0;

    } else {

        slot++;

    }

    tp = &cached_time[slot];

    tp->sec = sec;

    tp->msec = msec;

    ngx_gmtime(sec, &gmt);

    p0 = &cached_http_time[slot][0];

    (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",

                       week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,

                       months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,

                       gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);

#if (NGX_HAVE_GETTIMEZONE)

    tp->gmtoff = ngx_gettimezone();

    ngx_gmtime(sec + tp->gmtoff * 60, &tm);

#elif (NGX_HAVE_GMTOFF)

    ngx_localtime(sec, &tm);

    cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);

    tp->gmtoff = cached_gmtoff;

#else

    ngx_localtime(sec, &tm);

    cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);

    tp->gmtoff = cached_gmtoff;

#endif

    p1 = &cached_err_log_time[slot][0];

    (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",

                       tm.ngx_tm_year, tm.ngx_tm_mon,

                       tm.ngx_tm_mday, tm.ngx_tm_hour,

                       tm.ngx_tm_min, tm.ngx_tm_sec);

    p2 = &cached_http_log_time[slot][0];

    (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",

                       tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],

                       tm.ngx_tm_year, tm.ngx_tm_hour,

                       tm.ngx_tm_min, tm.ngx_tm_sec,

                       tp->gmtoff < 0 ? '-' : '+',

                       ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));

    p3 = &cached_http_log_iso8601[slot][0];

    (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",

                       tm.ngx_tm_year, tm.ngx_tm_mon,

                       tm.ngx_tm_mday, tm.ngx_tm_hour,

                       tm.ngx_tm_min, tm.ngx_tm_sec,

                       tp->gmtoff < 0 ? '-' : '+',

                       ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));

    p4 = &cached_syslog_time[slot][0];

    (void) ngx_sprintf(p4, "%s %2d %02d:%02d:%02d",

                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,

                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);

    ngx_memory_barrier();

    ngx_cached_time = tp;

    ngx_cached_http_time.data = p0;

    ngx_cached_err_log_time.data = p1;

    ngx_cached_http_log_time.data = p2;

    ngx_cached_http_log_iso8601.data = p3;

    ngx_cached_syslog_time.data = p4;

    ngx_unlock(&ngx_time_lock);

}

#if !(NGX_WIN32)

void

‌ngx_time_sigsafe_update(void)

{

    u_char          *p, *p2;

    ngx_tm_t         tm;

    time_t           sec;

    ngx_time_t      *tp;

    struct timeval   tv;

    if (!ngx_trylock(&ngx_time_lock)) {

        return;

    }

    ngx_gettimeofday(&tv);

    sec = tv.tv_sec;

    tp = &cached_time[slot];

    if (tp->sec == sec) {

        ngx_unlock(&ngx_time_lock);

        return;

    }

    if (slot == NGX_TIME_SLOTS - 1) {

        slot = 0;

    } else {

        slot++;

    }

    tp = &cached_time[slot];

    tp->sec = 0;

    ngx_gmtime(sec + cached_gmtoff * 60, &tm);

    p = &cached_err_log_time[slot][0];

    (void) ngx_sprintf(p, "%4d/%02d/%02d %02d:%02d:%02d",

                       tm.ngx_tm_year, tm.ngx_tm_mon,

                       tm.ngx_tm_mday, tm.ngx_tm_hour,

                       tm.ngx_tm_min, tm.ngx_tm_sec);

    p2 = &cached_syslog_time[slot][0];

    (void) ngx_sprintf(p2, "%s %2d %02d:%02d:%02d",

                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,

                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);

    ngx_memory_barrier();

    ngx_cached_err_log_time.data = p;

    ngx_cached_syslog_time.data = p2;

    ngx_unlock(&ngx_time_lock);

}

#endif

u_char *

‌ngx_http_time(u_char *buf, time_t t)

{

    ngx_tm_t  tm;

    ngx_gmtime(t, &tm);

    return ngx_sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT",

                       week[tm.ngx_tm_wday],

                       tm.ngx_tm_mday,

                       months[tm.ngx_tm_mon - 1],

                       tm.ngx_tm_year,

                       tm.ngx_tm_hour,

                       tm.ngx_tm_min,

                       tm.ngx_tm_sec);

}

u_char *

‌ngx_http_cookie_time(u_char *buf, time_t t)

{

    ngx_tm_t  tm;

    ngx_gmtime(t, &tm);

    /*

     * Netscape 3.x does not understand 4-digit years at all and

     * 2-digit years more than "37"

     */

    return ngx_sprintf(buf,

                       (tm.ngx_tm_year > 2037) ?

                                         "%s, %02d-%s-%d %02d:%02d:%02d GMT":

                                         "%s, %02d-%s-%02d %02d:%02d:%02d GMT",

                       week[tm.ngx_tm_wday],

                       tm.ngx_tm_mday,

                       months[tm.ngx_tm_mon - 1],

                       (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year:

                                                 tm.ngx_tm_year % 100,

                       tm.ngx_tm_hour,

                       tm.ngx_tm_min,

                       tm.ngx_tm_sec);

}

void

‌ngx_gmtime(time_t t, ngx_tm_t *tp)

{

    ngx_int_t   yday;

    ngx_uint_t  n, sec, min, hour, mday, mon, year, wday, days, leap;

    /* the calculation is valid for positive time_t only */

    n = (ngx_uint_t) t;

    days = n / 86400;

    /* January 1, 1970 was Thursday */

    wday = (4 + days) % 7;

    n %= 86400;

    hour = n / 3600;

    n %= 3600;

    min = n / 60;

    sec = n % 60;

    /*

     * the algorithm based on Gauss' formula,

     * see src/http/ngx_http_parse_time.c

     */

    /* days since March 1, 1 BC */

    days = days - (31 + 28) + 719527;

    /*

     * The "days" should be adjusted to 1 only, however, some March 1st's go

     * to previous year, so we adjust them to 2.  This causes also shift of the

     * last February days to next year, but we catch the case when "yday"

     * becomes negative.

     */

    year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);

    yday = days - (365 * year + year / 4 - year / 100 + year / 400);

    if (yday < 0) {

        leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));

        yday = 365 + leap + yday;

        year--;

    }

    /*

     * The empirical formula that maps "yday" to month.

     * There are at least 10 variants, some of them are:

     *     mon = (yday + 31) * 15 / 459

     *     mon = (yday + 31) * 17 / 520

     *     mon = (yday + 31) * 20 / 612

     */

    mon = (yday + 31) * 10 / 306;

    /* the Gauss' formula that evaluates days before the month */

    mday = yday - (367 * mon / 12 - 30) + 1;

    if (yday >= 306) {

        year++;

        mon -= 10;

        /*

         * there is no "yday" in Win32 SYSTEMTIME

         *

         * yday -= 306;

         */

    } else {

        mon += 2;

        /*

         * there is no "yday" in Win32 SYSTEMTIME

         *

         * yday += 31 + 28 + leap;

         */

    }

    tp->ngx_tm_sec = (ngx_tm_sec_t) sec;

    tp->ngx_tm_min = (ngx_tm_min_t) min;

    tp->ngx_tm_hour = (ngx_tm_hour_t) hour;

    tp->ngx_tm_mday = (ngx_tm_mday_t) mday;

    tp->ngx_tm_mon = (ngx_tm_mon_t) mon;

    tp->ngx_tm_year = (ngx_tm_year_t) year;

    tp->ngx_tm_wday = (ngx_tm_wday_t) wday;

}

time_t

‌ngx_next_time(time_t when)

{

    time_t     now, next;

    struct tm  tm;

    now = ngx_time();

    ngx_libc_localtime(now, &tm);

    tm.tm_hour = (int) (when / 3600);

    when %= 3600;

    tm.tm_min = (int) (when / 60);

    tm.tm_sec = (int) (when % 60);

    next = mktime(&tm);

    if (next == -1) {

        return -1;

    }

    if (next - now > 0) {

        return next;

    }

    tm.tm_mday++;

    /* mktime() should normalize a date (Jan 32, etc) */

    next = mktime(&tm);

    if (next != -1) {

        return next;

    }

    return -1;

}

src/os/unix/ngx_atomic.h - nginx-1.7.9

Data types defined

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_int_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

ngx_atomic_uint_t

Functions defined

ngx_atomic_cmp_set

ngx_atomic_fetch_add

Macros defined

AO_REQUIRE_CAS

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_ATOMIC_T_LEN

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

NGX_HAVE_ATOMIC_OPS

_NGX_ATOMIC_H_INCLUDED_

ngx_atomic_cmp_set

ngx_atomic_cmp_set

ngx_atomic_cmp_set

ngx_atomic_cmp_set

ngx_atomic_fetch_add

ngx_atomic_fetch_add

ngx_atomic_fetch_add

ngx_atomic_fetch_add

ngx_cpu_pause

ngx_cpu_pause

ngx_cpu_pause

ngx_cpu_pause

ngx_cpu_pause

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

ngx_trylock

ngx_unlock

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_ATOMIC_H_INCLUDED_

‌#define _NGX_ATOMIC_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_LIBATOMIC)

‌#define AO_REQUIRE_CAS

#include <atomic_ops.h>

‌#define NGX_HAVE_ATOMIC_OPS  1

‌typedef long                        ngx_atomic_int_t;

‌typedef AO_t                        ngx_atomic_uint_t;

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

#if (NGX_PTR_SIZE == 8)

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

#else

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

#endif

‌#define ngx_atomic_cmp_set(lock, old, new)                                    \

    AO_compare_and_swap(lock, old, new)

‌#define ngx_atomic_fetch_add(value, add)                                      \

    AO_fetch_and_add(value, add)

‌#define ngx_memory_barrier()        AO_nop()

‌#define ngx_cpu_pause()

#elif (NGX_DARWIN_ATOMIC)

/*

* use Darwin 8 atomic(3) and barrier(3) operations

* optimized at run-time for UP and SMP

*/

#include <libkern/OSAtomic.h>

/* "bool" conflicts with perl's CORE/handy.h */

#if 0

#undef bool

#endif

‌#define NGX_HAVE_ATOMIC_OPS  1

#if (NGX_PTR_SIZE == 8)

‌typedef int64_t                     ngx_atomic_int_t;

‌typedef uint64_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

‌#define ngx_atomic_cmp_set(lock, old, new)                                    \

    OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)

‌#define ngx_atomic_fetch_add(value, add)                                      \

    (OSAtomicAdd64(add, (int64_t *) value) - add)

#else

‌typedef int32_t                     ngx_atomic_int_t;

‌typedef uint32_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

‌#define ngx_atomic_cmp_set(lock, old, new)                                    \

    OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock)

‌#define ngx_atomic_fetch_add(value, add)                                      \

    (OSAtomicAdd32(add, (int32_t *) value) - add)

#endif

‌#define ngx_memory_barrier()        OSMemoryBarrier()

‌#define ngx_cpu_pause()

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

#elif (NGX_HAVE_GCC_ATOMIC)

/* GCC 4.1 builtin atomic operations */

‌#define NGX_HAVE_ATOMIC_OPS  1

‌typedef long                        ngx_atomic_int_t;

‌typedef unsigned long               ngx_atomic_uint_t;

#if (NGX_PTR_SIZE == 8)

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

#else

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

#endif

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

‌#define ngx_atomic_cmp_set(lock, old, set)                                    \

    __sync_bool_compare_and_swap(lock, old, set)

‌#define ngx_atomic_fetch_add(value, add)                                      \

    __sync_fetch_and_add(value, add)

‌#define ngx_memory_barrier()        __sync_synchronize()

#if ( __i386__ || __i386 || __amd64__ || __amd64 )

‌#define ngx_cpu_pause()             __asm__ ("pause")

#else

‌#define ngx_cpu_pause()

#endif

#elif ( __i386__ || __i386 )

‌typedef int32_t                     ngx_atomic_int_t;

‌typedef uint32_t                    ngx_atomic_uint_t;

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

#if ( __SUNPRO_C )

‌#define NGX_HAVE_ATOMIC_OPS  1

ngx_atomic_uint_t

ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set);

ngx_atomic_int_t

ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);

/*

* Sun Studio 12 exits with segmentation fault on '__asm ("pause")',

* so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il

*/

void

ngx_cpu_pause(void);

/* the code in src/os/unix/ngx_sunpro_x86.il */

‌#define ngx_memory_barrier()        __asm (".volatile"); __asm (".nonvolatile")

#else /* ( __GNUC__ || __INTEL_COMPILER ) */

‌#define NGX_HAVE_ATOMIC_OPS  1

#include "ngx_gcc_atomic_x86.h"

#endif

#elif ( __amd64__ || __amd64 )

‌typedef int64_t                     ngx_atomic_int_t;

‌typedef uint64_t                    ngx_atomic_uint_t;

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

#if ( __SUNPRO_C )

‌#define NGX_HAVE_ATOMIC_OPS  1

ngx_atomic_uint_t

ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set);

ngx_atomic_int_t

ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);

/*

* Sun Studio 12 exits with segmentation fault on '__asm ("pause")',

* so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il

*/

void

ngx_cpu_pause(void);

/* the code in src/os/unix/ngx_sunpro_amd64.il */

‌#define ngx_memory_barrier()        __asm (".volatile"); __asm (".nonvolatile")

#else /* ( __GNUC__ || __INTEL_COMPILER ) */

‌#define NGX_HAVE_ATOMIC_OPS  1

#include "ngx_gcc_atomic_amd64.h"

#endif

#elif ( __sparc__ || __sparc || __sparcv9 )

#if (NGX_PTR_SIZE == 8)

‌typedef int64_t                     ngx_atomic_int_t;

‌typedef uint64_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

#else

‌typedef int32_t                     ngx_atomic_int_t;

‌typedef uint32_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

#endif

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

#if ( __SUNPRO_C )

‌#define NGX_HAVE_ATOMIC_OPS  1

#include "ngx_sunpro_atomic_sparc64.h"

#else /* ( __GNUC__ || __INTEL_COMPILER ) */

‌#define NGX_HAVE_ATOMIC_OPS  1

#include "ngx_gcc_atomic_sparc64.h"

#endif

#elif ( __powerpc__ || __POWERPC__ )

‌#define NGX_HAVE_ATOMIC_OPS  1

#if (NGX_PTR_SIZE == 8)

‌typedef int64_t                     ngx_atomic_int_t;

‌typedef uint64_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)

#else

‌typedef int32_t                     ngx_atomic_int_t;

‌typedef uint32_t                    ngx_atomic_uint_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

#endif

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

#include "ngx_gcc_atomic_ppc.h"

#endif

#if !(NGX_HAVE_ATOMIC_OPS)

‌#define NGX_HAVE_ATOMIC_OPS  0

‌typedef int32_t                     ngx_atomic_int_t;

‌typedef uint32_t                    ngx_atomic_uint_t;

‌typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

‌#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

     ngx_atomic_uint_t set)

{

     if (*lock == old) {

         *lock = set;

         return 1;

     }

     return 0;

}

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

     ngx_atomic_int_t  old;

     old = *value;

     *value += add;

     return old;

}

‌#define ngx_memory_barrier()

‌#define ngx_cpu_pause()

#endif

void ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin);

‌#define ngx_trylock(lock)  (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))

‌#define ngx_unlock(lock)    *(lock) = 0

#endif /* _NGX_ATOMIC_H_INCLUDED_ */

src/os/unix/ngx_gcc_atomic_sparc64.h - nginx-1.7.9

Functions defined

ngx_atomic_cmp_set

ngx_atomic_fetch_add

Macros defined

NGX_CASA

NGX_CASA

ngx_cpu_pause

ngx_memory_barrier

ngx_memory_barrier

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

/*

* "casa   [r1] 0x80, r2, r0"  and

* "casxa  [r1] 0x80, r2, r0"  do the following:

*

*     if ([r1] == r2) {

*         swap(r0, [r1]);

*     } else {

*         r0 = [r1];

*     }

*

* so "r0 == r2" means that the operation was successful.

*

*

* The "r" means the general register.

* The "+r" means the general register used for both input and output.

*/

#if (NGX_PTR_SIZE == 4)

‌#define NGX_CASA  "casa"

#else

‌#define NGX_CASA  "casxa"

#endif

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    __asm__ volatile (

    NGX_CASA " [%1] 0x80, %2, %0"

    : "+r" (set) : "r" (lock), "r" (old) : "memory");

    return (set == old);

}

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    ngx_atomic_uint_t  old, res;

    old = *value;

    for ( ;; ) {

        res = old + add;

        __asm__ volatile (

        NGX_CASA " [%1] 0x80, %2, %0"

        : "+r" (res) : "r" (value), "r" (old) : "memory");

        if (res == old) {

            return res;

        }

        old = res;

    }

}

#if (NGX_SMP)

‌#define ngx_memory_barrier()                                                  \

            __asm__ volatile (                                                \

            "membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"        \

            ::: "memory")

#else

‌#define ngx_memory_barrier()   __asm__ volatile ("" ::: "memory")

#endif

‌#define ngx_cpu_pause()

src/core/ngx_spinlock.c - nginx-1.7.9

Functions defined

ngx_spinlock

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

void

‌ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)

{

#if (NGX_HAVE_ATOMIC_OPS)

    ngx_uint_t  i, n;

    for ( ;; ) {

        if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {

            return;

        }

        if (ngx_ncpu > 1) {

            for (n = 1; n < spin; n <<= 1) {

                for (i = 0; i < n; i++) {

                    ngx_cpu_pause();

                }

                if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {

                    return;

                }

            }

        }

        ngx_sched_yield();

    }

#else

#if (NGX_THREADS)

#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !

#endif

#endif

}

src/os/unix/ngx_posix_init.c - nginx-1.7.9

Global variables defined

ngx_inherited_nonblocking

ngx_max_sockets

ngx_ncpu

ngx_os_io

ngx_tcp_nodelay_and_tcp_nopush

rlmt

Functions defined

ngx_os_init

ngx_os_status

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <nginx.h>

‌ngx_int_t   ngx_ncpu;

‌ngx_int_t   ngx_max_sockets;

‌ngx_uint_t  ngx_inherited_nonblocking;

‌ngx_uint_t  ngx_tcp_nodelay_and_tcp_nopush;

‌struct rlimit  rlmt;

‌ngx_os_io_t ngx_os_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

    ngx_writev_chain,

    0

};

ngx_int_t

‌ngx_os_init(ngx_log_t *log)

{

    ngx_uint_t  n;

#if (NGX_HAVE_OS_SPECIFIC_INIT)

    if (ngx_os_specific_init(log) != NGX_OK) {

        return NGX_ERROR;

    }

#endif

    if (ngx_init_setproctitle(log) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_pagesize = getpagesize();

    ngx_cacheline_size = NGX_CPU_CACHE_LINE;

    for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }

#if (NGX_HAVE_SC_NPROCESSORS_ONLN)

    if (ngx_ncpu == 0) {

        ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);

    }

#endif

    if (ngx_ncpu < 1) {

        ngx_ncpu = 1;

    }

    ngx_cpuinfo();

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, errno,

                      "getrlimit(RLIMIT_NOFILE) failed)");

        return NGX_ERROR;

    }

    ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur;

#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)

    ngx_inherited_nonblocking = 1;

#else

    ngx_inherited_nonblocking = 0;

#endif

    srandom(ngx_time());

    return NGX_OK;

}

void

‌ngx_os_status(ngx_log_t *log)

{

    ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD);

#ifdef NGX_COMPILER

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "built by " NGX_COMPILER);

#endif

#if (NGX_HAVE_OS_SPECIFIC_INIT)

    ngx_os_specific_status(log);

#endif

    ngx_log_error(NGX_LOG_NOTICE, log, 0,

                  "getrlimit(RLIMIT_NOFILE): %r:%r",

                  rlmt.rlim_cur, rlmt.rlim_max);

}

#if 0

ngx_int_t

ngx_posix_post_conf_init(ngx_log_t *log)

{

    ngx_fd_t  pp[2];

    if (pipe(pp) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "pipe() failed");

        return NGX_ERROR;

    }

    if (dup2(pp[1], STDERR_FILENO) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, errno, "dup2(STDERR) failed");

        return NGX_ERROR;

    }

    if (pp[1] > STDERR_FILENO) {

        if (close(pp[1]) == -1) {

            ngx_log_error(NGX_LOG_EMERG, log, errno, "close() failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

#endif

src/os/unix/ngx_os.h - nginx-1.7.9

Data types defined

ngx_iovec_t

ngx_os_io_t

ngx_recv_chain_pt

ngx_recv_pt

ngx_send_chain_pt

ngx_send_pt

Macros defined

NGX_IOVS_PREALLOCATE

NGX_IOVS_PREALLOCATE

NGX_IO_SENDFILE

_NGX_OS_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_OS_H_INCLUDED_

‌#define _NGX_OS_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_IO_SENDFILE    1

‌typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);

‌typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

‌typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);

‌typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

typedef struct {

    ngx_recv_pt        recv;

    ngx_recv_chain_pt  recv_chain;

    ngx_recv_pt        udp_recv;

    ngx_send_pt        send;

    ngx_send_chain_pt  send_chain;

    ngx_uint_t         flags;

‌} ngx_os_io_t;

ngx_int_t ngx_os_init(ngx_log_t *log);

void ngx_os_status(ngx_log_t *log);

ngx_int_t ngx_os_specific_init(ngx_log_t *log);

void ngx_os_specific_status(ngx_log_t *log);

ngx_int_t ngx_daemon(ngx_log_t *log);

ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid);

ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);

ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry, off_t limit);

ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);

ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size);

ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

#if (NGX_HAVE_AIO)

ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size);

ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);

ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size);

ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

#endif

#if (IOV_MAX > 64)

‌#define NGX_IOVS_PREALLOCATE  64

#else

‌#define NGX_IOVS_PREALLOCATE  IOV_MAX

#endif

typedef struct {

    struct iovec  *iovs;

    ngx_uint_t     count;

    size_t         size;

    ngx_uint_t     nalloc;

‌} ngx_iovec_t;

ngx_chain_t *ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in,

    size_t limit, ngx_log_t *log);

ssize_t ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec);

extern ngx_os_io_t  ngx_os_io;

extern ngx_int_t    ngx_ncpu;

extern ngx_int_t    ngx_max_sockets;

extern ngx_uint_t   ngx_inherited_nonblocking;

extern ngx_uint_t   ngx_tcp_nodelay_and_tcp_nopush;

#if (NGX_FREEBSD)

#include <ngx_freebsd.h>

#elif (NGX_LINUX)

#include <ngx_linux.h>

#elif (NGX_SOLARIS)

#include <ngx_solaris.h>

#elif (NGX_DARWIN)

#include <ngx_darwin.h>

#endif

#endif /* _NGX_OS_H_INCLUDED_ */

src/os/unix/ngx_freebsd_init.c - nginx-1.7.9

Global variables defined

ngx_debug_malloc

ngx_freebsd_hw_ncpu

ngx_freebsd_io

ngx_freebsd_kern_ipc_somaxconn

ngx_freebsd_kern_osreldate

ngx_freebsd_kern_osrelease

ngx_freebsd_kern_ostype

ngx_freebsd_machdep_hlt_logical_cpus

ngx_freebsd_net_inet_tcp_sendspace

ngx_freebsd_sendfile_nbytes_bug

ngx_freebsd_use_tcp_nopush

sysctls

Data types defined

sysctl_t

Functions defined

ngx_debug_init

ngx_os_specific_init

ngx_os_specific_status

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/* FreeBSD 3.0 at least */

‌char    ngx_freebsd_kern_ostype[16];

‌char    ngx_freebsd_kern_osrelease[128];

‌int     ngx_freebsd_kern_osreldate;

‌int     ngx_freebsd_hw_ncpu;

‌int     ngx_freebsd_kern_ipc_somaxconn;

‌u_long  ngx_freebsd_net_inet_tcp_sendspace;

/* FreeBSD 4.9 */

‌int     ngx_freebsd_machdep_hlt_logical_cpus;

‌ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;

‌ngx_uint_t  ngx_freebsd_use_tcp_nopush;

‌ngx_uint_t  ngx_debug_malloc;

‌static ngx_os_io_t ngx_freebsd_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

#if (NGX_HAVE_SENDFILE)

    ngx_freebsd_sendfile_chain,

    NGX_IO_SENDFILE

#else

    ngx_writev_chain,

    0

#endif

};

typedef struct {

    char        *name;

    void        *value;

    size_t       size;

    ngx_uint_t   exists;

‌} sysctl_t;

‌sysctl_t sysctls[] = {

    { "hw.ncpu",

      &ngx_freebsd_hw_ncpu,

      sizeof(ngx_freebsd_hw_ncpu), 0 },

    { "machdep.hlt_logical_cpus",

      &ngx_freebsd_machdep_hlt_logical_cpus,

      sizeof(ngx_freebsd_machdep_hlt_logical_cpus), 0 },

    { "net.inet.tcp.sendspace",

      &ngx_freebsd_net_inet_tcp_sendspace,

      sizeof(ngx_freebsd_net_inet_tcp_sendspace), 0 },

    { "kern.ipc.somaxconn",

      &ngx_freebsd_kern_ipc_somaxconn,

      sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 },

    { NULL, NULL, 0, 0 }

};

void

‌ngx_debug_init(void)

{

#if (NGX_DEBUG_MALLOC)

#if __FreeBSD_version >= 500014 && __FreeBSD_version < 1000011

    _malloc_options = "J";

#elif __FreeBSD_version < 500014

    malloc_options = "J";

#endif

    ngx_debug_malloc = 1;

#else

    char  *mo;

    mo = getenv("MALLOC_OPTIONS");

    if (mo && ngx_strchr(mo, 'J')) {

        ngx_debug_malloc = 1;

    }

#endif

}

ngx_int_t

‌ngx_os_specific_init(ngx_log_t *log)

{

    int         version;

    size_t      size;

    ngx_err_t   err;

    ngx_uint_t  i;

    size = sizeof(ngx_freebsd_kern_ostype);

    if (sysctlbyname("kern.ostype",

                     ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysctlbyname(kern.ostype) failed");

        if (ngx_errno != NGX_ENOMEM) {

            return NGX_ERROR;

        }

        ngx_freebsd_kern_ostype[size - 1] = '\0';

    }

    size = sizeof(ngx_freebsd_kern_osrelease);

    if (sysctlbyname("kern.osrelease",

                     ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysctlbyname(kern.osrelease) failed");

        if (ngx_errno != NGX_ENOMEM) {

            return NGX_ERROR;

        }

        ngx_freebsd_kern_osrelease[size - 1] = '\0';

    }

    size = sizeof(int);

    if (sysctlbyname("kern.osreldate",

                     &ngx_freebsd_kern_osreldate, &size, NULL, 0) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysctlbyname(kern.osreldate) failed");

        return NGX_ERROR;

    }

    version = ngx_freebsd_kern_osreldate;

#if (NGX_HAVE_SENDFILE)

    /*

     * The determination of the sendfile() "nbytes bug" is complex enough.

     * There are two sendfile() syscalls: a new #393 has no bug while

     * an old #336 has the bug in some versions and has not in others.

     * Besides libc_r wrapper also emulates the bug in some versions.

     * There is no way to say exactly if syscall #336 in FreeBSD circa 4.6

     * has the bug.  We use the algorithm that is correct at least for

     * RELEASEs and for syscalls only (not libc_r wrapper).

     *

     * 4.6.1-RELEASE and below have the bug

     * 4.6.2-RELEASE and above have the new syscall

     *

     * We detect the new sendfile() syscall available at the compile time

     * to allow an old binary to run correctly on an updated FreeBSD system.

     */

#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \

    || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039

    /* a new syscall without the bug */

    ngx_freebsd_sendfile_nbytes_bug = 0;

#else

    /* an old syscall that may have the bug */

    ngx_freebsd_sendfile_nbytes_bug = 1;

#endif

#endif /* NGX_HAVE_SENDFILE */

    if ((version < 500000 && version >= 440003) || version >= 500017) {

        ngx_freebsd_use_tcp_nopush = 1;

    }

    for (i = 0; sysctls[i].name; i++) {

        size = sysctls[i].size;

        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)

            == 0)

        {

            sysctls[i].exists = 1;

            continue;

        }

        err = ngx_errno;

        if (err == NGX_ENOENT) {

            continue;

        }

        ngx_log_error(NGX_LOG_ALERT, log, err,

                      "sysctlbyname(%s) failed", sysctls[i].name);

        return NGX_ERROR;

    }

    if (ngx_freebsd_machdep_hlt_logical_cpus) {

        ngx_ncpu = ngx_freebsd_hw_ncpu / 2;

    } else {

        ngx_ncpu = ngx_freebsd_hw_ncpu;

    }

    if (version < 600008 && ngx_freebsd_kern_ipc_somaxconn > 32767) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "sysctl kern.ipc.somaxconn must be less than 32768");

        return NGX_ERROR;

    }

    ngx_tcp_nodelay_and_tcp_nopush = 1;

    ngx_os_io = ngx_freebsd_io;

    return NGX_OK;

}

void

‌ngx_os_specific_status(ngx_log_t *log)

{

    u_long      value;

    ngx_uint_t  i;

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",

                  ngx_freebsd_kern_ostype, ngx_freebsd_kern_osrelease);

#ifdef __DragonFly_version

    ngx_log_error(NGX_LOG_NOTICE, log, 0,

                  "kern.osreldate: %d, built on %d",

                  ngx_freebsd_kern_osreldate, __DragonFly_version);

#else

    ngx_log_error(NGX_LOG_NOTICE, log, 0,

                  "kern.osreldate: %d, built on %d",

                  ngx_freebsd_kern_osreldate, __FreeBSD_version);

#endif

    for (i = 0; sysctls[i].name; i++) {

        if (sysctls[i].exists) {

            if (sysctls[i].size == sizeof(long)) {

                value = *(long *) sysctls[i].value;

            } else {

                value = *(int *) sysctls[i].value;

            }

            ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",

                          sysctls[i].name, value);

        }

    }

}

src/os/unix/ngx_recv.c - nginx-1.7.9

Functions defined

ngx_unix_recv

ngx_unix_recv

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_HAVE_KQUEUE)

ssize_t

‌ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *rev;

    rev = c->read;

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: eof:%d, avail:%d, err:%d",

                       rev->pending_eof, rev->available, rev->kq_errno);

        if (rev->available == 0) {

            if (rev->pending_eof) {

                rev->ready = 0;

                rev->eof = 1;

                if (rev->kq_errno) {

                    rev->error = 1;

                    ngx_set_socket_errno(rev->kq_errno);

                    return ngx_connection_error(c, rev->kq_errno,

                               "kevent() reported about an closed connection");

                }

                return 0;

            } else {

                rev->ready = 0;

                return NGX_AGAIN;

            }

        }

    }

    do {

        n = recv(c->fd, buf, size, 0);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n >= 0) {

            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

                rev->available -= n;

                /*

                 * rev->available may be negative here because some additional

                 * bytes may be received between kevent() and recv()

                 */

                if (rev->available <= 0) {

                    if (!rev->pending_eof) {

                        rev->ready = 0;

                    }

                    if (rev->available < 0) {

                        rev->available = 0;

                    }

                }

                if (n == 0) {

                    /*

                     * on FreeBSD recv() may return 0 on closed socket

                     * even if kqueue reported about available data

                     */

                    rev->ready = 0;

                    rev->eof = 1;

                    rev->available = 0;

                }

                return n;

            }

            if ((size_t) n < size

                && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))

            {

                rev->ready = 0;

            }

            if (n == 0) {

                rev->eof = 1;

            }

            return n;

        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "recv() not ready");

            n = NGX_AGAIN;

        } else {

            n = ngx_connection_error(c, err, "recv() failed");

            break;

        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {

        rev->error = 1;

    }

    return n;

}

#else /* ! NGX_HAVE_KQUEUE */

ssize_t

‌ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *rev;

    rev = c->read;

    do {

        n = recv(c->fd, buf, size, 0);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n == 0) {

            rev->ready = 0;

            rev->eof = 1;

            return n;

        } else if (n > 0) {

            if ((size_t) n < size

                && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))

            {

                rev->ready = 0;

            }

            return n;

        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "recv() not ready");

            n = NGX_AGAIN;

        } else {

            n = ngx_connection_error(c, err, "recv() failed");

            break;

        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {

        rev->error = 1;

    }

    return n;

}

#endif /* NGX_HAVE_KQUEUE */

src/event/ngx_event.c - nginx-1.7.9

Global variables defined

connection_counter

event_core_name

ngx_accept_disabled

ngx_accept_events

ngx_accept_mutex

ngx_accept_mutex_delay

ngx_accept_mutex_held

ngx_accept_mutex_ptr

ngx_connection_counter

ngx_event_actions

ngx_event_core_commands

ngx_event_core_module

ngx_event_core_module_ctx

ngx_event_flags

ngx_event_max_module

ngx_event_timer_alarm

ngx_events_commands

ngx_events_module

ngx_events_module_ctx

ngx_stat_accepted

ngx_stat_accepted0

ngx_stat_active

ngx_stat_active0

ngx_stat_handled

ngx_stat_handled0

ngx_stat_reading

ngx_stat_reading0

ngx_stat_requests

ngx_stat_requests0

ngx_stat_waiting

ngx_stat_waiting0

ngx_stat_writing

ngx_stat_writing0

ngx_timer_resolution

ngx_use_accept_mutex

Functions defined

ngx_event_connections

ngx_event_core_create_conf

ngx_event_core_init_conf

ngx_event_debug_connection

ngx_event_init_conf

ngx_event_module_init

ngx_event_process_init

ngx_event_use

ngx_events_block

ngx_handle_read_event

ngx_handle_write_event

ngx_process_events_and_timers

ngx_send_lowat

ngx_timer_signal_handler

Macros defined

DEFAULT_CONNECTIONS

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define DEFAULT_CONNECTIONS  512

extern ngx_module_t ngx_kqueue_module;

extern ngx_module_t ngx_eventport_module;

extern ngx_module_t ngx_devpoll_module;

extern ngx_module_t ngx_epoll_module;

extern ngx_module_t ngx_rtsig_module;

extern ngx_module_t ngx_select_module;

static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf);

static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);

static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);

static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static void *ngx_event_core_create_conf(ngx_cycle_t *cycle);

static char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf);

‌static ngx_uint_t     ngx_timer_resolution;

‌sig_atomic_t          ngx_event_timer_alarm;

‌static ngx_uint_t     ngx_event_max_module;

‌ngx_uint_t            ngx_event_flags;

‌ngx_event_actions_t   ngx_event_actions;

‌static ngx_atomic_t   connection_counter = 1;

‌ngx_atomic_t         *ngx_connection_counter = &connection_counter;

‌ngx_atomic_t         *ngx_accept_mutex_ptr;

‌ngx_shmtx_t           ngx_accept_mutex;

‌ngx_uint_t            ngx_use_accept_mutex;

‌ngx_uint_t            ngx_accept_events;

‌ngx_uint_t            ngx_accept_mutex_held;

‌ngx_msec_t            ngx_accept_mutex_delay;

‌ngx_int_t             ngx_accept_disabled;

#if (NGX_STAT_STUB)

‌ngx_atomic_t   ngx_stat_accepted0;

‌ngx_atomic_t  *ngx_stat_accepted = &ngx_stat_accepted0;

‌ngx_atomic_t   ngx_stat_handled0;

‌ngx_atomic_t  *ngx_stat_handled = &ngx_stat_handled0;

‌ngx_atomic_t   ngx_stat_requests0;

‌ngx_atomic_t  *ngx_stat_requests = &ngx_stat_requests0;

‌ngx_atomic_t   ngx_stat_active0;

‌ngx_atomic_t  *ngx_stat_active = &ngx_stat_active0;

‌ngx_atomic_t   ngx_stat_reading0;

‌ngx_atomic_t  *ngx_stat_reading = &ngx_stat_reading0;

‌ngx_atomic_t   ngx_stat_writing0;

‌ngx_atomic_t  *ngx_stat_writing = &ngx_stat_writing0;

‌ngx_atomic_t   ngx_stat_waiting0;

‌ngx_atomic_t  *ngx_stat_waiting = &ngx_stat_waiting0;

#endif

‌static ngx_command_t  ngx_events_commands[] = {

    { ngx_string("events"),

      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_events_block,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_core_module_t  ngx_events_module_ctx = {

    ngx_string("events"),

    NULL,

    ngx_event_init_conf

};

‌ngx_module_t  ngx_events_module = {

    NGX_MODULE_V1,

    &ngx_events_module_ctx,                /* module context */

    ngx_events_commands,                   /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  event_core_name = ngx_string("event_core");

‌static ngx_command_t  ngx_event_core_commands[] = {

    { ngx_string("worker_connections"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_event_connections,

      0,

      0,

      NULL },

    { ngx_string("connections"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_event_connections,

      0,

      0,

      NULL },

    { ngx_string("use"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_event_use,

      0,

      0,

      NULL },

    { ngx_string("multi_accept"),

      NGX_EVENT_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      0,

      offsetof(ngx_event_conf_t, multi_accept),

      NULL },

    { ngx_string("accept_mutex"),

      NGX_EVENT_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      0,

      offsetof(ngx_event_conf_t, accept_mutex),

      NULL },

    { ngx_string("accept_mutex_delay"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      0,

      offsetof(ngx_event_conf_t, accept_mutex_delay),

      NULL },

    { ngx_string("debug_connection"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_event_debug_connection,

      0,

      0,

      NULL },

      ngx_null_command

};

‌ngx_event_module_t  ngx_event_core_module_ctx = {

    &event_core_name,

    ngx_event_core_create_conf,            /* create configuration */

    ngx_event_core_init_conf,              /* init configuration */

    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }

};

‌ngx_module_t  ngx_event_core_module = {

    NGX_MODULE_V1,

    &ngx_event_core_module_ctx,            /* module context */

    ngx_event_core_commands,               /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    ngx_event_module_init,                 /* init module */

    ngx_event_process_init,                /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

void

‌ngx_process_events_and_timers(ngx_cycle_t *cycle)

{

    ngx_uint_t  flags;

    ngx_msec_t  timer, delta;

    if (ngx_timer_resolution) {

        timer = NGX_TIMER_INFINITE;

        flags = 0;

    } else {

        timer = ngx_event_find_timer();

        flags = NGX_UPDATE_TIME;

#if (NGX_THREADS)

        if (timer == NGX_TIMER_INFINITE || timer > 500) {

            timer = 500;

        }

#endif

    }

    if (ngx_use_accept_mutex) {

        if (ngx_accept_disabled > 0) {

            ngx_accept_disabled--;

        } else {

            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {

                return;

            }

            if (ngx_accept_mutex_held) {

                flags |= NGX_POST_EVENTS;

            } else {

                if (timer == NGX_TIMER_INFINITE

                    || timer > ngx_accept_mutex_delay)

                {

                    timer = ngx_accept_mutex_delay;

                }

            }

        }

    }

    delta = ngx_current_msec;

    (void) ngx_process_events(cycle, timer, flags);

    delta = ngx_current_msec - delta;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "timer delta: %M", delta);

    ngx_event_process_posted(cycle, &ngx_posted_accept_events);

    if (ngx_accept_mutex_held) {

        ngx_shmtx_unlock(&ngx_accept_mutex);

    }

    if (delta) {

        ngx_event_expire_timers();

    }

    ngx_event_process_posted(cycle, &ngx_posted_events);

}

ngx_int_t

‌ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)

{

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue, epoll */

        if (!rev->active && !rev->ready) {

            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

        }

        return NGX_OK;

    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {

        /* select, poll, /dev/poll */

        if (!rev->active && !rev->ready) {

            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

        if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {

            if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {

        /* event ports */

        if (!rev->active && !rev->ready) {

            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

        if (rev->oneshot && !rev->ready) {

            if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

    }

    /* aio, iocp, rtsig */

    return NGX_OK;

}

ngx_int_t

‌ngx_handle_write_event(ngx_event_t *wev, size_t lowat)

{

    ngx_connection_t  *c;

    if (lowat) {

        c = wev->data;

        if (ngx_send_lowat(c, lowat) == NGX_ERROR) {

            return NGX_ERROR;

        }

    }

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue, epoll */

        if (!wev->active && !wev->ready) {

            if (ngx_add_event(wev, NGX_WRITE_EVENT,

                              NGX_CLEAR_EVENT | (lowat ? NGX_LOWAT_EVENT : 0))

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

        }

        return NGX_OK;

    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {

        /* select, poll, /dev/poll */

        if (!wev->active && !wev->ready) {

            if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

        if (wev->active && wev->ready) {

            if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {

        /* event ports */

        if (!wev->active && !wev->ready) {

            if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

        if (wev->oneshot && wev->ready) {

            if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

    }

    /* aio, iocp, rtsig */

    return NGX_OK;

}

static char *

‌ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)

{

    if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                      "no \"events\" section in configuration");

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_event_module_init(ngx_cycle_t *cycle)

{

    void              ***cf;

    u_char              *shared;

    size_t               size, cl;

    ngx_shm_t            shm;

    ngx_time_t          *tp;

    ngx_core_conf_t     *ccf;

    ngx_event_conf_t    *ecf;

    cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);

    ecf = (*cf)[ngx_event_core_module.ctx_index];

    if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {

        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,

                      "using the \"%s\" event method", ecf->name);

    }

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_timer_resolution = ccf->timer_resolution;

#if !(NGX_WIN32)

    {

    ngx_int_t      limit;

    struct rlimit  rlmt;

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "getrlimit(RLIMIT_NOFILE) failed, ignored");

    } else {

        if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur

            && (ccf->rlimit_nofile == NGX_CONF_UNSET

                || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))

        {

            limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?

                         (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;

            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,

                          "%ui worker_connections exceed "

                          "open file resource limit: %i",

                          ecf->connections, limit);

        }

    }

    }

#endif /* !(NGX_WIN32) */

    if (ccf->master == 0) {

        return NGX_OK;

    }

    if (ngx_accept_mutex_ptr) {

        return NGX_OK;

    }

    /* cl should be equal to or greater than cache line size */

    cl = 128;

    size = cl            /* ngx_accept_mutex */

           + cl          /* ngx_connection_counter */

           + cl;         /* ngx_temp_number */

#if (NGX_STAT_STUB)

    size += cl           /* ngx_stat_accepted */

           + cl          /* ngx_stat_handled */

           + cl          /* ngx_stat_requests */

           + cl          /* ngx_stat_active */

           + cl          /* ngx_stat_reading */

           + cl          /* ngx_stat_writing */

           + cl;         /* ngx_stat_waiting */

#endif

    shm.size = size;

    shm.name.len = sizeof("nginx_shared_zone");

    shm.name.data = (u_char *) "nginx_shared_zone";

    shm.log = cycle->log;

    if (ngx_shm_alloc(&shm) != NGX_OK) {

        return NGX_ERROR;

    }

    shared = shm.addr;

    ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;

    ngx_accept_mutex.spin = (ngx_uint_t) -1;

    if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,

                         cycle->lock_file.data)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);

    (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "counter: %p, %d",

                   ngx_connection_counter, *ngx_connection_counter);

    ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);

    tp = ngx_timeofday();

    ngx_random_number = (tp->msec << 16) + ngx_pid;

#if (NGX_STAT_STUB)

    ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);

    ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);

    ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);

    ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);

    ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);

    ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);

    ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);

#endif

    return NGX_OK;

}

#if !(NGX_WIN32)

static void

‌ngx_timer_signal_handler(int signo)

{

    ngx_event_timer_alarm = 1;

#if 1

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal");

#endif

}

#endif

static ngx_int_t

‌ngx_event_process_init(ngx_cycle_t *cycle)

{

    ngx_uint_t           m, i;

    ngx_event_t         *rev, *wev;

    ngx_listening_t     *ls;

    ngx_connection_t    *c, *next, *old;

    ngx_core_conf_t     *ccf;

    ngx_event_conf_t    *ecf;

    ngx_event_module_t  *module;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {

        ngx_use_accept_mutex = 1;

        ngx_accept_mutex_held = 0;

        ngx_accept_mutex_delay = ecf->accept_mutex_delay;

    } else {

        ngx_use_accept_mutex = 0;

    }

#if (NGX_WIN32)

    /*

     * disable accept mutex on win32 as it may cause deadlock if

     * grabbed by a process which can't accept connections

     */

    ngx_use_accept_mutex = 0;

#endif

    ngx_queue_init(&ngx_posted_accept_events);

    ngx_queue_init(&ngx_posted_events);

    if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {

        return NGX_ERROR;

    }

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {

            continue;

        }

        if (ngx_modules[m]->ctx_index != ecf->use) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {

            /* fatal */

            exit(2);

        }

        break;

    }

#if !(NGX_WIN32)

    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {

        struct sigaction  sa;

        struct itimerval  itv;

        ngx_memzero(&sa, sizeof(struct sigaction));

        sa.sa_handler = ngx_timer_signal_handler;

        sigemptyset(&sa.sa_mask);

        if (sigaction(SIGALRM, &sa, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "sigaction(SIGALRM) failed");

            return NGX_ERROR;

        }

        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;

        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;

        itv.it_value.tv_sec = ngx_timer_resolution / 1000;

        itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;

        if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setitimer() failed");

        }

    }

    if (ngx_event_flags & NGX_USE_FD_EVENT) {

        struct rlimit  rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "getrlimit(RLIMIT_NOFILE) failed");

            return NGX_ERROR;

        }

        cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;

        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,

                                  cycle->log);

        if (cycle->files == NULL) {

            return NGX_ERROR;

        }

    }

#endif

    cycle->connections =

        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);

    if (cycle->connections == NULL) {

        return NGX_ERROR;

    }

    c = cycle->connections;

    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,

                                   cycle->log);

    if (cycle->read_events == NULL) {

        return NGX_ERROR;

    }

    rev = cycle->read_events;

    for (i = 0; i < cycle->connection_n; i++) {

        rev[i].closed = 1;

        rev[i].instance = 1;

    }

    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,

                                    cycle->log);

    if (cycle->write_events == NULL) {

        return NGX_ERROR;

    }

    wev = cycle->write_events;

    for (i = 0; i < cycle->connection_n; i++) {

        wev[i].closed = 1;

    }

    i = cycle->connection_n;

    next = NULL;

    do {

        i--;

        c[i].data = next;

        c[i].read = &cycle->read_events[i];

        c[i].write = &cycle->write_events[i];

        c[i].fd = (ngx_socket_t) -1;

        next = &c[i];

#if (NGX_THREADS)

        c[i].lock = 0;

#endif

    } while (i);

    cycle->free_connections = next;

    cycle->free_connection_n = cycle->connection_n;

    /* for each listening socket */

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        c = ngx_get_connection(ls[i].fd, cycle->log);

        if (c == NULL) {

            return NGX_ERROR;

        }

        c->log = &ls[i].log;

        c->listening = &ls[i];

        ls[i].connection = c;

        rev = c->read;

        rev->log = c->log;

        rev->accept = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT)

        rev->deferred_accept = ls[i].deferred_accept;

#endif

        if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {

            if (ls[i].previous) {

                /*

                 * delete the old accept events that were bound to

                 * the old cycle read events array

                 */

                old = ls[i].previous->connection;

                if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)

                    == NGX_ERROR)

                {

                    return NGX_ERROR;

                }

                old->fd = (ngx_socket_t) -1;

            }

        }

#if (NGX_WIN32)

        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {

            ngx_iocp_conf_t  *iocpcf;

            rev->handler = ngx_event_acceptex;

            if (ngx_use_accept_mutex) {

                continue;

            }

            if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {

                return NGX_ERROR;

            }

            ls[i].log.handler = ngx_acceptex_log_error;

            iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);

            if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

        } else {

            rev->handler = ngx_event_accept;

            if (ngx_use_accept_mutex) {

                continue;

            }

            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

#else

        rev->handler = ngx_event_accept;

        if (ngx_use_accept_mutex) {

            continue;

        }

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

            if (ngx_add_conn(c) == NGX_ERROR) {

                return NGX_ERROR;

            }

        } else {

            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

#endif

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_send_lowat(ngx_connection_t *c, size_t lowat)

{

    int  sndlowat;

#if (NGX_HAVE_LOWAT_EVENT)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        c->write->available = lowat;

        return NGX_OK;

    }

#endif

    if (lowat == 0 || c->sndlowat) {

        return NGX_OK;

    }

    sndlowat = (int) lowat;

    if (setsockopt(c->fd, SOL_SOCKET, SO_SNDLOWAT,

                   (const void *) &sndlowat, sizeof(int))

        == -1)

    {

        ngx_connection_error(c, ngx_socket_errno,

                             "setsockopt(SO_SNDLOWAT) failed");

        return NGX_ERROR;

    }

    c->sndlowat = 1;

    return NGX_OK;

}

static char *

‌ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                 *rv;

    void               ***ctx;

    ngx_uint_t            i;

    ngx_conf_t            pcf;

    ngx_event_module_t   *m;

    if (*(void **) conf) {

        return "is duplicate";

    }

    /* count the number of the event modules and set up their indices */

    ngx_event_max_module = 0;

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        ngx_modules[i]->ctx_index = ngx_event_max_module++;

    }

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));

    if (*ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *(void **) conf = ctx;

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        m = ngx_modules[i]->ctx;

        if (m->create_conf) {

            (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);

            if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

    }

    pcf = *cf;

    cf->ctx = ctx;

    cf->module_type = NGX_EVENT_MODULE;

    cf->cmd_type = NGX_EVENT_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)

        return rv;

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        m = ngx_modules[i]->ctx;

        if (m->init_conf) {

            rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);

            if (rv != NGX_CONF_OK) {

                return rv;

            }

        }

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_event_conf_t  *ecf = conf;

    ngx_str_t  *value;

    if (ecf->connections != NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    if (ngx_strcmp(cmd->name.data, "connections") == 0) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "the \"connections\" directive is deprecated, "

                           "use the \"worker_connections\" directive instead");

    }

    value = cf->args->elts;

    ecf->connections = ngx_atoi(value[1].data, value[1].len);

    if (ecf->connections == (ngx_uint_t) NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid number \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    cf->cycle->connection_n = ecf->connections;

    return NGX_CONF_OK;

}

static char *

‌ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_event_conf_t  *ecf = conf;

    ngx_int_t             m;

    ngx_str_t            *value;

    ngx_event_conf_t     *old_ecf;

    ngx_event_module_t   *module;

    if (ecf->use != NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (cf->cycle->old_cycle->conf_ctx) {

        old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,

                                     ngx_event_core_module);

    } else {

        old_ecf = NULL;

    }

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->name->len == value[1].len) {

            if (ngx_strcmp(module->name->data, value[1].data) == 0) {

                ecf->use = ngx_modules[m]->ctx_index;

                ecf->name = module->name->data;

                if (ngx_process == NGX_PROCESS_SINGLE

                    && old_ecf

                    && old_ecf->use != ecf->use)

                {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "when the server runs without a master process "

                               "the \"%V\" event type must be the same as "

                               "in previous configuration - \"%s\" "

                               "and it cannot be changed on the fly, "

                               "to change it you need to stop server "

                               "and start it again",

                               &value[1], old_ecf->name);

                    return NGX_CONF_ERROR;

                }

                return NGX_CONF_OK;

            }

        }

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid event type \"%V\"", &value[1]);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

#if (NGX_DEBUG)

    ngx_event_conf_t  *ecf = conf;

    ngx_int_t             rc;

    ngx_str_t            *value;

    ngx_url_t             u;

    ngx_cidr_t            c, *cidr;

    ngx_uint_t            i;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    value = cf->args->elts;

#if (NGX_HAVE_UNIX_DOMAIN)

    if (ngx_strcmp(value[1].data, "unix:") == 0) {

        cidr = ngx_array_push(&ecf->debug_connection);

        if (cidr == NULL) {

            return NGX_CONF_ERROR;

        }

        cidr->family = AF_UNIX;

        return NGX_CONF_OK;

    }

#endif

    rc = ngx_ptocidr(&value[1], &c);

    if (rc != NGX_ERROR) {

        if (rc == NGX_DONE) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "low address bits of %V are meaningless",

                               &value[1]);

        }

        cidr = ngx_array_push(&ecf->debug_connection);

        if (cidr == NULL) {

            return NGX_CONF_ERROR;

        }

        *cidr = c;

        return NGX_CONF_OK;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.host = value[1];

    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "%s in debug_connection \"%V\"",

                               u.err, &u.host);

        }

        return NGX_CONF_ERROR;

    }

    cidr = ngx_array_push_n(&ecf->debug_connection, u.naddrs);

    if (cidr == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));

    for (i = 0; i < u.naddrs; i++) {

        cidr[i].family = u.addrs[i].sockaddr->sa_family;

        switch (cidr[i].family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;

            cidr[i].u.in6.addr = sin6->sin6_addr;

            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;

            cidr[i].u.in.addr = sin->sin_addr.s_addr;

            cidr[i].u.in.mask = 0xffffffff;

            break;

        }

    }

#else

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"debug_connection\" is ignored, you need to rebuild "

                       "nginx using --with-debug option to enable it");

#endif

    return NGX_CONF_OK;

}

static void *

‌ngx_event_core_create_conf(ngx_cycle_t *cycle)

{

    ngx_event_conf_t  *ecf;

    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));

    if (ecf == NULL) {

        return NULL;

    }

    ecf->connections = NGX_CONF_UNSET_UINT;

    ecf->use = NGX_CONF_UNSET_UINT;

    ecf->multi_accept = NGX_CONF_UNSET;

    ecf->accept_mutex = NGX_CONF_UNSET;

    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;

    ecf->name = (void *) NGX_CONF_UNSET;

#if (NGX_DEBUG)

    if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,

                       sizeof(ngx_cidr_t)) == NGX_ERROR)

    {

        return NULL;

    }

#endif

    return ecf;

}

static char *

‌ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_event_conf_t  *ecf = conf;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

    int                  fd;

#endif

#if (NGX_HAVE_RTSIG)

    ngx_uint_t           rtsig;

    ngx_core_conf_t     *ccf;

#endif

    ngx_int_t            i;

    ngx_module_t        *module;

    ngx_event_module_t  *event_module;

    module = NULL;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

    fd = epoll_create(100);

    if (fd != -1) {

        (void) close(fd);

        module = &ngx_epoll_module;

    } else if (ngx_errno != NGX_ENOSYS) {

        module = &ngx_epoll_module;

    }

#endif

#if (NGX_HAVE_RTSIG)

    if (module == NULL) {

        module = &ngx_rtsig_module;

        rtsig = 1;

    } else {

        rtsig = 0;

    }

#endif

#if (NGX_HAVE_DEVPOLL)

    module = &ngx_devpoll_module;

#endif

#if (NGX_HAVE_KQUEUE)

    module = &ngx_kqueue_module;

#endif

#if (NGX_HAVE_SELECT)

    if (module == NULL) {

        module = &ngx_select_module;

    }

#endif

    if (module == NULL) {

        for (i = 0; ngx_modules[i]; i++) {

            if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

                continue;

            }

            event_module = ngx_modules[i]->ctx;

            if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)

            {

                continue;

            }

            module = ngx_modules[i];

            break;

        }

    }

    if (module == NULL) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");

        return NGX_CONF_ERROR;

    }

    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);

    cycle->connection_n = ecf->connections;

    ngx_conf_init_uint_value(ecf->use, module->ctx_index);

    event_module = module->ctx;

    ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

    ngx_conf_init_value(ecf->multi_accept, 0);

    ngx_conf_init_value(ecf->accept_mutex, 1);

    ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

#if (NGX_HAVE_RTSIG)

    if (!rtsig) {

        return NGX_CONF_OK;

    }

    if (ecf->accept_mutex) {

        return NGX_CONF_OK;

    }

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ccf->worker_processes == 0) {

        return NGX_CONF_OK;

    }

    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                  "the \"rtsig\" method requires \"accept_mutex\" to be on");

    return NGX_CONF_ERROR;

#else

    return NGX_CONF_OK;

#endif

}

src/event/modules/ngx_kqueue_module.c - nginx-1.7.9

Global variables defined

change_list

change_list0

change_list1

event_list

kevent_mutex

kqueue_name

list_mutex

max_changes

nchanges

nevents

ngx_kqueue

ngx_kqueue_commands

ngx_kqueue_module

ngx_kqueue_module_ctx

Data types defined

ngx_kqueue_conf_t

Functions defined

ngx_kqueue_add_event

ngx_kqueue_create_conf

ngx_kqueue_del_event

ngx_kqueue_done

ngx_kqueue_dump_event

ngx_kqueue_init

ngx_kqueue_init_conf

ngx_kqueue_process_changes

ngx_kqueue_process_events

ngx_kqueue_set_event

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

typedef struct {

    ngx_uint_t  changes;

    ngx_uint_t  events;

‌} ngx_kqueue_conf_t;

static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_kqueue_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter,

    ngx_uint_t flags);

static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try);

static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log,

    struct kevent *kev);

static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle);

static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf);

‌int                    ngx_kqueue = -1;

/*

* The "change_list" should be declared as ngx_thread_volatile.

* However, the use of the change_list is localized in kqueue functions and

* is protected by the mutex so even the "icc -ipo" should not build the code

* with the race condition.  Thus we avoid the declaration to make a more

* readable code.

*/

‌‌‌static struct kevent  *change_list, *change_list0, *change_list1;

‌static struct kevent  *event_list;

‌‌‌static ngx_uint_t      max_changes, nchanges, nevents;

#if (NGX_THREADS)

‌static ngx_mutex_t    *list_mutex;

‌static ngx_mutex_t    *kevent_mutex;

#endif

‌static ngx_str_t      kqueue_name = ngx_string("kqueue");

‌static ngx_command_t  ngx_kqueue_commands[] = {

    { ngx_string("kqueue_changes"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_kqueue_conf_t, changes),

      NULL },

    { ngx_string("kqueue_events"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_kqueue_conf_t, events),

      NULL },

      ngx_null_command

};

‌ngx_event_module_t  ngx_kqueue_module_ctx = {

    &kqueue_name,

    ngx_kqueue_create_conf,                /* create configuration */

    ngx_kqueue_init_conf,                  /* init configuration */

    {

        ngx_kqueue_add_event,              /* add an event */

        ngx_kqueue_del_event,              /* delete an event */

        ngx_kqueue_add_event,              /* enable an event */

        ngx_kqueue_del_event,              /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        ngx_kqueue_process_changes,        /* process the changes */

        ngx_kqueue_process_events,         /* process the events */

        ngx_kqueue_init,                   /* init the events */

        ngx_kqueue_done                    /* done the events */

    }

};

‌ngx_module_t  ngx_kqueue_module = {

    NGX_MODULE_V1,

    &ngx_kqueue_module_ctx,                /* module context */

    ngx_kqueue_commands,                   /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    ngx_kqueue_conf_t  *kcf;

    struct timespec     ts;

#if (NGX_HAVE_TIMER_EVENT)

    struct kevent       kev;

#endif

    kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module);

    if (ngx_kqueue == -1) {

        ngx_kqueue = kqueue();

        if (ngx_kqueue == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "kqueue() failed");

            return NGX_ERROR;

        }

#if (NGX_THREADS)

        list_mutex = ngx_mutex_init(cycle->log, 0);

        if (list_mutex == NULL) {

            return NGX_ERROR;

        }

        kevent_mutex = ngx_mutex_init(cycle->log, 0);

        if (kevent_mutex == NULL) {

            return NGX_ERROR;

        }

#endif

    }

    if (max_changes < kcf->changes) {

        if (nchanges) {

            ts.tv_sec = 0;

            ts.tv_nsec = 0;

            if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                              "kevent() failed");

                return NGX_ERROR;

            }

            nchanges = 0;

        }

        if (change_list0) {

            ngx_free(change_list0);

        }

        change_list0 = ngx_alloc(kcf->changes * sizeof(struct kevent),

                                 cycle->log);

        if (change_list0 == NULL) {

            return NGX_ERROR;

        }

        if (change_list1) {

            ngx_free(change_list1);

        }

        change_list1 = ngx_alloc(kcf->changes * sizeof(struct kevent),

                                 cycle->log);

        if (change_list1 == NULL) {

            return NGX_ERROR;

        }

        change_list = change_list0;

    }

    max_changes = kcf->changes;

    if (nevents < kcf->events) {

        if (event_list) {

            ngx_free(event_list);

        }

        event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log);

        if (event_list == NULL) {

            return NGX_ERROR;

        }

    }

    ngx_event_flags = NGX_USE_ONESHOT_EVENT

                      |NGX_USE_KQUEUE_EVENT

                      |NGX_USE_VNODE_EVENT;

#if (NGX_HAVE_TIMER_EVENT)

    if (timer) {

        kev.ident = 0;

        kev.filter = EVFILT_TIMER;

        kev.flags = EV_ADD|EV_ENABLE;

        kev.fflags = 0;

        kev.data = timer;

        kev.udata = 0;

        ts.tv_sec = 0;

        ts.tv_nsec = 0;

        if (kevent(ngx_kqueue, &kev, 1, NULL, 0, &ts) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "kevent(EVFILT_TIMER) failed");

            return NGX_ERROR;

        }

        ngx_event_flags |= NGX_USE_TIMER_EVENT;

    }

#endif

#if (NGX_HAVE_CLEAR_EVENT)

    ngx_event_flags |= NGX_USE_CLEAR_EVENT;

#else

    ngx_event_flags |= NGX_USE_LEVEL_EVENT;

#endif

#if (NGX_HAVE_LOWAT_EVENT)

    ngx_event_flags |= NGX_USE_LOWAT_EVENT;

#endif

    nevents = kcf->events;

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_kqueue_module_ctx.actions;

    return NGX_OK;

}

static void

‌ngx_kqueue_done(ngx_cycle_t *cycle)

{

    if (close(ngx_kqueue) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "kqueue close() failed");

    }

    ngx_kqueue = -1;

#if (NGX_THREADS)

    ngx_mutex_destroy(kevent_mutex);

    ngx_mutex_destroy(list_mutex);

#endif

    ngx_free(change_list1);

    ngx_free(change_list0);

    ngx_free(event_list);

    change_list1 = NULL;

    change_list0 = NULL;

    change_list = NULL;

    event_list = NULL;

    max_changes = 0;

    nchanges = 0;

    nevents = 0;

}

static ngx_int_t

‌ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_int_t          rc;

#if 0

    ngx_event_t       *e;

    ngx_connection_t  *c;

#endif

    ev->active = 1;

    ev->disabled = 0;

    ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;

    ngx_mutex_lock(list_mutex);

#if 0

    if (ev->index < nchanges

        && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)

            == (uintptr_t) ev)

    {

        if (change_list[ev->index].flags == EV_DISABLE) {

            /*

             * if the EV_DISABLE is still not passed to a kernel

             * we will not pass it

             */

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                           "kevent activated: %d: ft:%i",

                           ngx_event_ident(ev->data), event);

            if (ev->index < --nchanges) {

                e = (ngx_event_t *)

                    ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);

                change_list[ev->index] = change_list[nchanges];

                e->index = ev->index;

            }

            ngx_mutex_unlock(list_mutex);

            return NGX_OK;

        }

        c = ev->data;

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "previous event on #%d were not passed in kernel", c->fd);

        ngx_mutex_unlock(list_mutex);

        return NGX_ERROR;

    }

#endif

    rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags);

    ngx_mutex_unlock(list_mutex);

    return rc;

}

static ngx_int_t

‌ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_int_t     rc;

    ngx_event_t  *e;

    ev->active = 0;

    ev->disabled = 0;

    ngx_mutex_lock(list_mutex);

    if (ev->index < nchanges

        && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)

            == (uintptr_t) ev)

    {

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "kevent deleted: %d: ft:%i",

                       ngx_event_ident(ev->data), event);

        /* if the event is still not passed to a kernel we will not pass it */

        nchanges--;

        if (ev->index < nchanges) {

            e = (ngx_event_t *)

                    ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);

            change_list[ev->index] = change_list[nchanges];

            e->index = ev->index;

        }

        ngx_mutex_unlock(list_mutex);

        return NGX_OK;

    }

    /*

     * when the file descriptor is closed the kqueue automatically deletes

     * its filters so we do not need to delete explicitly the event

     * before the closing the file descriptor.

     */

    if (flags & NGX_CLOSE_EVENT) {

        ngx_mutex_unlock(list_mutex);

        return NGX_OK;

    }

    if (flags & NGX_DISABLE_EVENT) {

        ev->disabled = 1;

    } else {

        flags |= EV_DELETE;

    }

    rc = ngx_kqueue_set_event(ev, event, flags);

    ngx_mutex_unlock(list_mutex);

    return rc;

}

static ngx_int_t

‌ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags)

{

    struct kevent     *kev;

    struct timespec    ts;

    ngx_connection_t  *c;

    c = ev->data;

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "kevent set event: %d: ft:%i fl:%04Xi",

                   c->fd, filter, flags);

    if (nchanges >= max_changes) {

        ngx_log_error(NGX_LOG_WARN, ev->log, 0,

                      "kqueue change list is filled up");

        ts.tv_sec = 0;

        ts.tv_nsec = 0;

        if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");

            return NGX_ERROR;

        }

        nchanges = 0;

    }

    kev = &change_list[nchanges];

    kev->ident = c->fd;

    kev->filter = (short) filter;

    kev->flags = (u_short) flags;

    kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance);

    if (filter == EVFILT_VNODE) {

        kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND

                                 |NOTE_ATTRIB|NOTE_RENAME

#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \

    || __FreeBSD_version >= 500018

                                 |NOTE_REVOKE

#endif

                      ;

        kev->data = 0;

    } else {

#if (NGX_HAVE_LOWAT_EVENT)

        if (flags & NGX_LOWAT_EVENT) {

            kev->fflags = NOTE_LOWAT;

            kev->data = ev->available;

        } else {

            kev->fflags = 0;

            kev->data = 0;

        }

#else

        kev->fflags = 0;

        kev->data = 0;

#endif

    }

    ev->index = nchanges;

    nchanges++;

    if (flags & NGX_FLUSH_EVENT) {

        ts.tv_sec = 0;

        ts.tv_nsec = 0;

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush");

        if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");

            return NGX_ERROR;

        }

        nchanges = 0;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int               events, n;

    ngx_int_t         i, instance;

    ngx_uint_t        level;

    ngx_err_t         err;

    ngx_event_t      *ev;

    ngx_queue_t      *queue;

    struct timespec   ts, *tp;

    if (ngx_threaded) {

        if (ngx_kqueue_process_changes(cycle, 0) == NGX_ERROR) {

            return NGX_ERROR;

        }

        n = 0;

    } else {

        n = (int) nchanges;

        nchanges = 0;

    }

    if (timer == NGX_TIMER_INFINITE) {

        tp = NULL;

    } else {

        ts.tv_sec = timer / 1000;

        ts.tv_nsec = (timer % 1000) * 1000000;

        /*

         * 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is

         * the int32_t while user level ts.tv_nsec is the long (64-bit),

         * so on the big endian PowerPC all nanoseconds are lost.

         */

#if (NGX_DARWIN_KEVENT_BUG)

        ts.tv_nsec <<= 32;

#endif

        tp = &ts;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "kevent timer: %M, changes: %d", timer, n);

    events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp);

    err = (events == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

        ngx_time_update();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "kevent events: %d", events);

    if (err) {

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {

                ngx_event_timer_alarm = 0;

                return NGX_OK;

            }

            level = NGX_LOG_INFO;

        } else {

            level = NGX_LOG_ALERT;

        }

        ngx_log_error(level, cycle->log, err, "kevent() failed");

        return NGX_ERROR;

    }

    if (events == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "kevent() returned no events without timeout");

        return NGX_ERROR;

    }

    for (i = 0; i < events; i++) {

        ngx_kqueue_dump_event(cycle->log, &event_list[i]);

        if (event_list[i].flags & EV_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data,

                          "kevent() error on %d filter:%d flags:%04Xd",

                          event_list[i].ident, event_list[i].filter,

                          event_list[i].flags);

            continue;

        }

#if (NGX_HAVE_TIMER_EVENT)

        if (event_list[i].filter == EVFILT_TIMER) {

            ngx_time_update();

            continue;

        }

#endif

        ev = (ngx_event_t *) event_list[i].udata;

        switch (event_list[i].filter) {

        case EVFILT_READ:

        case EVFILT_WRITE:

            instance = (uintptr_t) ev & 1;

            ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);

            if (ev->closed || ev->instance != instance) {

                /*

                 * the stale event from a file descriptor

                 * that was just closed in this iteration

                 */

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "kevent: stale event %p", ev);

                continue;

            }

            if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {

                ngx_kqueue_dump_event(ev->log, &event_list[i]);

            }

            if (ev->oneshot) {

                ev->active = 0;

            }

            ev->available = event_list[i].data;

            if (event_list[i].flags & EV_EOF) {

                ev->pending_eof = 1;

                ev->kq_errno = event_list[i].fflags;

            }

            ev->ready = 1;

            break;

        case EVFILT_VNODE:

            ev->kq_vnode = 1;

            break;

        case EVFILT_AIO:

            ev->complete = 1;

            ev->ready = 1;

            break;

        default:

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "unexpected kevent() filter %d",

                          event_list[i].filter);

            continue;

        }

        if (flags & NGX_POST_EVENTS) {

            queue = ev->accept ? &ngx_posted_accept_events

                               : &ngx_posted_events;

            ngx_post_event(ev, queue);

            continue;

        }

        ev->handler(ev);

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try)

{

    int               n;

    ngx_int_t         rc;

    ngx_err_t         err;

    struct timespec   ts;

    struct kevent    *changes;

    ngx_mutex_lock(kevent_mutex);

    ngx_mutex_lock(list_mutex);

    if (nchanges == 0) {

        ngx_mutex_unlock(list_mutex);

        ngx_mutex_unlock(kevent_mutex);

        return NGX_OK;

    }

    changes = change_list;

    if (change_list == change_list0) {

        change_list = change_list1;

    } else {

        change_list = change_list0;

    }

    n = (int) nchanges;

    nchanges = 0;

    ngx_mutex_unlock(list_mutex);

    ts.tv_sec = 0;

    ts.tv_nsec = 0;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "kevent changes: %d", n);

    if (kevent(ngx_kqueue, changes, n, NULL, 0, &ts) == -1) {

        err = ngx_errno;

        ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,

                      cycle->log, err, "kevent() failed");

        rc = NGX_ERROR;

    } else {

        rc = NGX_OK;

    }

    ngx_mutex_unlock(kevent_mutex);

    return rc;

}

static ngx_inline void

‌ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev)

{

    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,

                   (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) ?

                    "kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p":

                    "kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p",

                    kev->ident, kev->filter,

                    kev->flags, kev->fflags,

                    kev->data, kev->udata);

}

static void *

‌ngx_kqueue_create_conf(ngx_cycle_t *cycle)

{

    ngx_kqueue_conf_t  *kcf;

    kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t));

    if (kcf == NULL) {

        return NULL;

    }

    kcf->changes = NGX_CONF_UNSET;

    kcf->events = NGX_CONF_UNSET;

    return kcf;

}

static char *

‌ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_kqueue_conf_t *kcf = conf;

    ngx_conf_init_uint_value(kcf->changes, 512);

    ngx_conf_init_uint_value(kcf->events, 512);

    return NGX_CONF_OK;

}

src/os/unix/ngx_thread.h - nginx-1.7.9

Data types defined

ngx_cond_t

ngx_mutex_t

ngx_thread_t

ngx_thread_value_t

ngx_tid_t

ngx_tls_key_t

Macros defined

NGX_MAX_THREADS

NGX_MUTEX_LIGHT

NGX_THREAD_BUSY

NGX_THREAD_DONE

NGX_THREAD_EXIT

NGX_THREAD_FREE

NGX_TID_T_FMT

NGX_TID_T_FMT

NGX_TID_T_FMT

_NGX_THREAD_H_INCLUDED_

ngx_cond_signal

ngx_log_tid

ngx_log_tid

ngx_mutex_lock

ngx_mutex_trylock

ngx_mutex_unlock

ngx_setthrtitle

ngx_thread_get_tls

ngx_thread_join

ngx_thread_key_create

ngx_thread_key_create_n

ngx_thread_main

ngx_thread_self

ngx_thread_set_tls

ngx_thread_set_tls_n

ngx_thread_sigmask

ngx_thread_sigmask_n

ngx_thread_volatile

ngx_thread_volatile

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_THREAD_H_INCLUDED_

‌#define _NGX_THREAD_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_THREADS)

‌#define NGX_MAX_THREADS      128

#if (NGX_USE_RFORK)

#include <ngx_freebsd_rfork_thread.h>

#else /* use pthreads */

#include <pthread.h>

‌typedef pthread_t                    ngx_tid_t;

‌#define ngx_thread_self()            pthread_self()

‌#define ngx_log_tid                  (int) ngx_thread_self()

#if (NGX_FREEBSD) && !(NGX_LINUXTHREADS)

‌#define NGX_TID_T_FMT                "%p"

#else

‌#define NGX_TID_T_FMT                "%d"

#endif

‌typedef pthread_key_t                ngx_tls_key_t;

‌#define ngx_thread_key_create(key)   pthread_key_create(key, NULL)

‌#define ngx_thread_key_create_n      "pthread_key_create()"

‌#define ngx_thread_set_tls           pthread_setspecific

‌#define ngx_thread_set_tls_n         "pthread_setspecific()"

‌#define ngx_thread_get_tls           pthread_getspecific

‌#define NGX_MUTEX_LIGHT     0

typedef struct {

    pthread_mutex_t   mutex;

    ngx_log_t        *log;

‌} ngx_mutex_t;

typedef struct {

    pthread_cond_t    cond;

    ngx_log_t        *log;

‌} ngx_cond_t;

‌#define ngx_thread_sigmask     pthread_sigmask

‌#define ngx_thread_sigmask_n  "pthread_sigmask()"

‌#define ngx_thread_join(t, p)  pthread_join(t, p)

‌#define ngx_setthrtitle(n)

ngx_int_t ngx_mutex_trylock(ngx_mutex_t *m);

void ngx_mutex_lock(ngx_mutex_t *m);

void ngx_mutex_unlock(ngx_mutex_t *m);

#endif

‌#define ngx_thread_volatile   volatile

typedef struct {

    ngx_tid_t    tid;

    ngx_cond_t  *cv;

    ngx_uint_t   state;

‌} ngx_thread_t;

‌#define NGX_THREAD_FREE   1

‌#define NGX_THREAD_BUSY   2

‌#define NGX_THREAD_EXIT   3

‌#define NGX_THREAD_DONE   4

extern ngx_int_t              ngx_threads_n;

extern volatile ngx_thread_t  ngx_threads[NGX_MAX_THREADS];

‌typedef void *  ngx_thread_value_t;

ngx_int_t ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle);

ngx_err_t ngx_create_thread(ngx_tid_t *tid,

    ngx_thread_value_t (*func)(void *arg), void *arg, ngx_log_t *log);

ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags);

void ngx_mutex_destroy(ngx_mutex_t *m);

ngx_cond_t *ngx_cond_init(ngx_log_t *log);

void ngx_cond_destroy(ngx_cond_t *cv);

ngx_int_t ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m);

ngx_int_t ngx_cond_signal(ngx_cond_t *cv);

#else /* !NGX_THREADS */

‌#define ngx_thread_volatile

‌#define ngx_log_tid           0

‌#define NGX_TID_T_FMT         "%d"

‌#define ngx_mutex_trylock(m)  NGX_OK

‌#define ngx_mutex_lock(m)

‌#define ngx_mutex_unlock(m)

‌#define ngx_cond_signal(cv)

‌#define ngx_thread_main()     1

#endif

#endif /* _NGX_THREAD_H_INCLUDED_ */

src/os/unix/ngx_freebsd_rfork_thread.c - nginx-1.7.9

Global variables defined

errno0

errnos

last_stack

max_threads

ngx_freebsd_kern_usrstack

ngx_thread_stack_size

ngx_tls

nkeys

nthreads

rz_size

tids

usable_stack_size

Functions defined

__error

_spinlock

_spinunlock

ngx_cond_destroy

ngx_cond_init

ngx_cond_signal

ngx_cond_wait

ngx_create_thread

ngx_init_threads

ngx_mutex_destroy

ngx_mutex_dolock

ngx_mutex_init

ngx_mutex_unlock

ngx_thread_key_create

ngx_thread_self

ngx_thread_set_tls

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* The threads implementation uses the rfork(RFPROC|RFTHREAD|RFMEM) syscall

* to create threads.  All threads use the stacks of the same size mmap()ed

* below the main stack.  Thus the current thread id is determined via

* the stack pointer value.

*

* The mutex implementation uses the ngx_atomic_cmp_set() operation

* to acquire a mutex and the SysV semaphore to wait on a mutex and to wake up

* the waiting threads.  The light mutex does not use semaphore, so after

* spinning in the lock the thread calls sched_yield().  However the light

* mutexes are intended to be used with the "trylock" operation only.

* The SysV semop() is a cheap syscall, particularly if it has little sembuf's

* and does not use SEM_UNDO.

*

* The condition variable implementation uses the signal #64.

* The signal handler is SIG_IGN so the kill() is a cheap syscall.

* The thread waits a signal in kevent().  The use of the EVFILT_SIGNAL

* is safe since FreeBSD 4.10-STABLE.

*

* This threads implementation currently works on i386 (486+) and amd64

* platforms only.

*/

‌char                 *ngx_freebsd_kern_usrstack;

‌size_t                ngx_thread_stack_size;

‌static size_t         rz_size;

‌static size_t         usable_stack_size;

‌static char          *last_stack;

‌static ngx_uint_t     nthreads;

‌static ngx_uint_t     max_threads;

‌static ngx_uint_t     nkeys;

‌static ngx_tid_t     *tids;      /* the threads tids array */

‌void                **ngx_tls;   /* the threads tls's array */

/* the thread-safe libc errno */

‌static int   errno0;   /* the main thread's errno */

‌static int  *errnos;   /* the threads errno's array */

int *

‌__error()

{

    int  tid;

    tid = ngx_gettid();

    return tid ? &errnos[tid - 1] : &errno0;

}

/*

* __isthreaded enables the spinlocks in some libc functions, i.e. in malloc()

* and some other places.  Nevertheless we protect our malloc()/free() calls

* by own mutex that is more efficient than the spinlock.

*

* _spinlock() is a weak referenced stub in src/lib/libc/gen/_spinlock_stub.c

* that does nothing.

*/

extern int  __isthreaded;

void

‌_spinlock(ngx_atomic_t *lock)

{

    ngx_int_t  tries;

    tries = 0;

    for ( ;; ) {

        if (*lock) {

            if (ngx_ncpu > 1 && tries++ < 1000) {

                continue;

            }

            sched_yield();

            tries = 0;

        } else {

            if (ngx_atomic_cmp_set(lock, 0, 1)) {

                return;

            }

        }

    }

}

/*

* Before FreeBSD 5.1 _spinunlock() is a simple #define in

* src/lib/libc/include/spinlock.h that zeroes lock.

*

* Since FreeBSD 5.1 _spinunlock() is a weak referenced stub in

* src/lib/libc/gen/_spinlock_stub.c that does nothing.

*/

#ifndef _spinunlock

void

‌_spinunlock(ngx_atomic_t *lock)

{

    *lock = 0;

}

#endif

ngx_err_t

‌ngx_create_thread(ngx_tid_t *tid, ngx_thread_value_t (*func)(void *arg),

    void *arg, ngx_log_t *log)

{

    ngx_pid_t   id;

    ngx_err_t   err;

    char       *stack, *stack_top;

    if (nthreads >= max_threads) {

        ngx_log_error(NGX_LOG_CRIT, log, 0,

                      "no more than %ui threads can be created", max_threads);

        return NGX_ERROR;

    }

    last_stack -= ngx_thread_stack_size;

    stack = mmap(last_stack, usable_stack_size, PROT_READ|PROT_WRITE,

                 MAP_STACK, -1, 0);

    if (stack == MAP_FAILED) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "mmap(%p:%uz, MAP_STACK) thread stack failed",

                      last_stack, usable_stack_size);

        return NGX_ERROR;

    }

    if (stack != last_stack) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "stack %p address was changed to %p", last_stack, stack);

        return NGX_ERROR;

    }

    stack_top = stack + usable_stack_size;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,

                   "thread stack: %p-%p", stack, stack_top);

    ngx_set_errno(0);

    id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top,

                      (ngx_rfork_thread_func_pt) func, arg);

    err = ngx_errno;

    if (id == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, err, "rfork() failed");

    } else {

        *tid = id;

        nthreads = (ngx_freebsd_kern_usrstack - stack_top)

                                                       / ngx_thread_stack_size;

        tids[nthreads] = id;

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "rfork()ed thread: %P", id);

    }

    return err;

}

ngx_int_t

‌ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle)

{

    char              *red_zone, *zone;

    size_t             len;

    ngx_int_t          i;

    struct sigaction   sa;

    max_threads = n + 1;

    for (i = 0; i < n; i++) {

        ngx_memzero(&sa, sizeof(struct sigaction));

        sa.sa_handler = SIG_IGN;

        sigemptyset(&sa.sa_mask);

        if (sigaction(NGX_CV_SIGNAL, &sa, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "sigaction(%d, SIG_IGN) failed", NGX_CV_SIGNAL);

            return NGX_ERROR;

        }

    }

    len = sizeof(ngx_freebsd_kern_usrstack);

    if (sysctlbyname("kern.usrstack", &ngx_freebsd_kern_usrstack, &len,

                                                                NULL, 0) == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "sysctlbyname(kern.usrstack) failed");

        return NGX_ERROR;

    }

    /* the main thread stack red zone */

    rz_size = ngx_pagesize;

    red_zone = ngx_freebsd_kern_usrstack - (size + rz_size);

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                   "usrstack: %p red zone: %p",

                   ngx_freebsd_kern_usrstack, red_zone);

    zone = mmap(red_zone, rz_size, PROT_NONE, MAP_ANON, -1, 0);

    if (zone == MAP_FAILED) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "mmap(%p:%uz, PROT_NONE, MAP_ANON) red zone failed",

                      red_zone, rz_size);

        return NGX_ERROR;

    }

    if (zone != red_zone) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "red zone %p address was changed to %p", red_zone, zone);

        return NGX_ERROR;

    }

    /* create the thread errno' array */

    errnos = ngx_calloc(n * sizeof(int), cycle->log);

    if (errnos == NULL) {

        return NGX_ERROR;

    }

    /* create the thread tids array */

    tids = ngx_calloc((n + 1) * sizeof(ngx_tid_t), cycle->log);

    if (tids == NULL) {

        return NGX_ERROR;

    }

    tids[0] = ngx_pid;

    /* create the thread tls' array */

    ngx_tls = ngx_calloc(NGX_THREAD_KEYS_MAX * (n + 1) * sizeof(void *),

                         cycle->log);

    if (ngx_tls == NULL) {

        return NGX_ERROR;

    }

    nthreads = 1;

    last_stack = zone + rz_size;

    usable_stack_size = size;

    ngx_thread_stack_size = size + rz_size;

    /* allow the spinlock in libc malloc() */

    __isthreaded = 1;

    ngx_threaded = 1;

    return NGX_OK;

}

ngx_tid_t

‌ngx_thread_self(void)

{

    ngx_int_t  tid;

    tid = ngx_gettid();

    if (tids == NULL) {

        return ngx_pid;

    }

    return tids[tid];

}

ngx_err_t

‌ngx_thread_key_create(ngx_tls_key_t *key)

{

    if (nkeys >= NGX_THREAD_KEYS_MAX) {

        return NGX_ENOMEM;

    }

    *key = nkeys++;

    return 0;

}

ngx_err_t

‌ngx_thread_set_tls(ngx_tls_key_t key, void *value)

{

    if (key >= NGX_THREAD_KEYS_MAX) {

        return NGX_EINVAL;

    }

    ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()] = value;

    return 0;

}

ngx_mutex_t *

‌ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags)

{

    ngx_mutex_t  *m;

    union semun   op;

    m = ngx_alloc(sizeof(ngx_mutex_t), log);

    if (m == NULL) {

        return NULL;

    }

    m->lock = 0;

    m->log = log;

    if (flags & NGX_MUTEX_LIGHT) {

        m->semid = -1;

        return m;

    }

    m->semid = semget(IPC_PRIVATE, 1, SEM_R|SEM_A);

    if (m->semid == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semget() failed");

        return NULL;

    }

    op.val = 0;

    if (semctl(m->semid, 0, SETVAL, op) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semctl(SETVAL) failed");

        if (semctl(m->semid, 0, IPC_RMID) == -1) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          "semctl(IPC_RMID) failed");

        }

        return NULL;

    }

    return m;

}

void

‌ngx_mutex_destroy(ngx_mutex_t *m)

{

    if (semctl(m->semid, 0, IPC_RMID) == -1) {

        ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,

                      "semctl(IPC_RMID) failed");

    }

    ngx_free((void *) m);

}

ngx_int_t

‌ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try)

{

    uint32_t       lock, old;

    ngx_uint_t     tries;

    struct sembuf  op;

    if (!ngx_threaded) {

        return NGX_OK;

    }

#if (NGX_DEBUG)

    if (try) {

        ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                       "try lock mutex %p lock:%XD", m, m->lock);

    } else {

        ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                       "lock mutex %p lock:%XD", m, m->lock);

    }

#endif

    old = m->lock;

    tries = 0;

    for ( ;; ) {

        if (old & NGX_MUTEX_LOCK_BUSY) {

            if (try) {

                return NGX_AGAIN;

            }

            if (ngx_ncpu > 1 && tries++ < 1000) {

                /* the spinlock is used only on the SMP system */

                old = m->lock;

                continue;

            }

            if (m->semid == -1) {

                sched_yield();

                tries = 0;

                old = m->lock;

                continue;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                           "mutex %p lock:%XD", m, m->lock);

            /*

             * The mutex is locked so we increase a number

             * of the threads that are waiting on the mutex

             */

            lock = old + 1;

            if ((lock & ~NGX_MUTEX_LOCK_BUSY) > nthreads) {

                ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,

                              "%D threads wait for mutex %p, "

                              "while only %ui threads are available",

                              lock & ~NGX_MUTEX_LOCK_BUSY, m, nthreads);

                ngx_abort();

            }

            if (ngx_atomic_cmp_set(&m->lock, old, lock)) {

                ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                               "wait mutex %p lock:%XD", m, m->lock);

                /*

                 * The number of the waiting threads has been increased

                 * and we would wait on the SysV semaphore.

                 * A semaphore should wake up us more efficiently than

                 * a simple sched_yield() or usleep().

                 */

                op.sem_num = 0;

                op.sem_op = -1;

                op.sem_flg = 0;

                if (semop(m->semid, &op, 1) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,

                                 "semop() failed while waiting on mutex %p", m);

                    ngx_abort();

                }

                ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                               "mutex waked up %p lock:%XD", m, m->lock);

                tries = 0;

                old = m->lock;

                continue;

            }

            old = m->lock;

        } else {

            lock = old | NGX_MUTEX_LOCK_BUSY;

            if (ngx_atomic_cmp_set(&m->lock, old, lock)) {

                /* we locked the mutex */

                break;

            }

            old = m->lock;

        }

        if (tries++ > 1000) {

            ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                           "mutex %p is contested", m);

            /* the mutex is probably contested so we are giving up now */

            sched_yield();

            tries = 0;

            old = m->lock;

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                   "mutex %p is locked, lock:%XD", m, m->lock);

    return NGX_OK;

}

void

‌ngx_mutex_unlock(ngx_mutex_t *m)

{

    uint32_t       lock, old;

    struct sembuf  op;

    if (!ngx_threaded) {

        return;

    }

    old = m->lock;

    if (!(old & NGX_MUTEX_LOCK_BUSY)) {

        ngx_log_error(NGX_LOG_ALERT, m->log, 0,

                      "trying to unlock the free mutex %p", m);

        ngx_abort();

    }

    /* free the mutex */

#if 0

    ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                   "unlock mutex %p lock:%XD", m, old);

#endif

    for ( ;; ) {

        lock = old & ~NGX_MUTEX_LOCK_BUSY;

        if (ngx_atomic_cmp_set(&m->lock, old, lock)) {

            break;

        }

        old = m->lock;

    }

    if (m->semid == -1) {

        ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                       "mutex %p is unlocked", m);

        return;

    }

    /* check whether we need to wake up a waiting thread */

    old = m->lock;

    for ( ;; ) {

        if (old & NGX_MUTEX_LOCK_BUSY) {

            /* the mutex is just locked by another thread */

            break;

        }

        if (old == 0) {

            break;

        }

        /* there are the waiting threads */

        lock = old - 1;

        if (ngx_atomic_cmp_set(&m->lock, old, lock)) {

            /* wake up the thread that waits on semaphore */

            ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                           "wake up mutex %p", m);

            op.sem_num = 0;

            op.sem_op = 1;

            op.sem_flg = 0;

            if (semop(m->semid, &op, 1) == -1) {

                ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,

                              "semop() failed while waking up on mutex %p", m);

                ngx_abort();

            }

            break;

        }

        old = m->lock;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,

                   "mutex %p is unlocked", m);

    return;

}

ngx_cond_t *

‌ngx_cond_init(ngx_log_t *log)

{

    ngx_cond_t  *cv;

    cv = ngx_alloc(sizeof(ngx_cond_t), log);

    if (cv == NULL) {

        return NULL;

    }

    cv->signo = NGX_CV_SIGNAL;

    cv->tid = -1;

    cv->log = log;

    cv->kq = -1;

    return cv;

}

void

‌ngx_cond_destroy(ngx_cond_t *cv)

{

    if (close(cv->kq) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno,

                      "kqueue close() failed");

    }

    ngx_free(cv);

}

ngx_int_t

‌ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m)

{

    int              n;

    ngx_err_t        err;

    struct kevent    kev;

    struct timespec  ts;

    if (cv->kq == -1) {

        /*

         * We have to add the EVFILT_SIGNAL filter in the rfork()ed thread.

         * Otherwise the thread would not get a signal event.

         *

         * However, we have not to open the kqueue in the thread,

         * it is simply handy do it together.

         */

        cv->kq = kqueue();

        if (cv->kq == -1) {

            ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kqueue() failed");

            return NGX_ERROR;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0,

                       "cv kq:%d signo:%d", cv->kq, cv->signo);

        kev.ident = cv->signo;

        kev.filter = EVFILT_SIGNAL;

        kev.flags = EV_ADD;

        kev.fflags = 0;

        kev.data = 0;

        kev.udata = NULL;

        ts.tv_sec = 0;

        ts.tv_nsec = 0;

        if (kevent(cv->kq, &kev, 1, NULL, 0, &ts) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kevent() failed");

            return NGX_ERROR;

        }

        cv->tid = ngx_thread_self();

    }

    ngx_mutex_unlock(m);

    ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0,

                   "cv %p wait, kq:%d, signo:%d", cv, cv->kq, cv->signo);

    for ( ;; ) {

        n = kevent(cv->kq, NULL, 0, &kev, 1, NULL);

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0,

                       "cv %p kevent: %d", cv, n);

        if (n == -1) {

            err = ngx_errno;

            ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,

                          cv->log, ngx_errno,

                          "kevent() failed while waiting condition variable %p",

                          cv);

            if (err == NGX_EINTR) {

                break;

            }

            return NGX_ERROR;

        }

        if (n == 0) {

            ngx_log_error(NGX_LOG_ALERT, cv->log, 0,

                          "kevent() returned no events "

                          "while waiting condition variable %p",

                          cv);

            continue;

        }

        if (kev.filter != EVFILT_SIGNAL) {

            ngx_log_error(NGX_LOG_ALERT, cv->log, 0,

                          "kevent() returned unexpected events: %d "

                          "while waiting condition variable %p",

                          kev.filter, cv);

            continue;

        }

        if (kev.ident != (uintptr_t) cv->signo) {

            ngx_log_error(NGX_LOG_ALERT, cv->log, 0,

                          "kevent() returned unexpected signal: %d ",

                          "while waiting condition variable %p",

                          kev.ident, cv);

            continue;

        }

        break;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is waked up", cv);

    ngx_mutex_lock(m);

    return NGX_OK;

}

ngx_int_t

‌ngx_cond_signal(ngx_cond_t *cv)

{

    ngx_err_t  err;

    ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0,

                   "cv %p to signal %P %d",

                   cv, cv->tid, cv->signo);

    if (cv->tid == -1) {

        return NGX_OK;

    }

    if (kill(cv->tid, cv->signo) == -1) {

        err = ngx_errno;

        ngx_log_error(NGX_LOG_ALERT, cv->log, err,

                     "kill() failed while signaling condition variable %p", cv);

        if (err == NGX_ESRCH) {

            cv->tid = -1;

        }

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is signaled", cv);

    return NGX_OK;

}

src/os/unix/ngx_freebsd_rfork_thread.h - nginx-1.7.9

Data types defined

ngx_cond_t

ngx_mutex_t

ngx_rfork_thread_func_pt

ngx_tid_t

ngx_tls_key_t

Functions defined

ngx_gettid

ngx_thread_get_tls

Macros defined

NGX_CV_SIGNAL

NGX_MUTEX_LIGHT

NGX_MUTEX_LOCK_BUSY

NGX_THREAD_KEYS_MAX

NGX_TID_T_FMT

_NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_

ngx_log_pid

ngx_log_tid

ngx_mutex_lock

ngx_mutex_trylock

ngx_setthrtitle

ngx_thread_join

ngx_thread_key_create_n

ngx_thread_set_tls_n

ngx_thread_sigmask

ngx_thread_sigmask_n

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_

‌#define _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_

#include <sys/ipc.h>

#include <sys/sem.h>

#include <sched.h>

‌typedef pid_t  ngx_tid_t;

‌#define ngx_log_pid    ngx_thread_self()

‌#define ngx_log_tid    0

‌#define NGX_TID_T_FMT  "%P"

‌#define NGX_MUTEX_LIGHT      1

‌#define NGX_MUTEX_LOCK_BUSY  0x80000000

typedef volatile struct {

    ngx_atomic_t  lock;

    ngx_log_t    *log;

    int           semid;

‌} ngx_mutex_t;

‌#define NGX_CV_SIGNAL        64

typedef struct {

    int           signo;

    int           kq;

    ngx_tid_t     tid;

    ngx_log_t    *log;

‌} ngx_cond_t;

‌#define ngx_thread_sigmask(how, set, oset)                         \

            (sigprocmask(how, set, oset) == -1) ? ngx_errno : 0

‌#define ngx_thread_sigmask_n   "sigprocmask()"

‌#define ngx_thread_join(t, p)

‌#define ngx_setthrtitle(n)     setproctitle(n)

extern char    *ngx_freebsd_kern_usrstack;

extern size_t   ngx_thread_stack_size;

static ngx_inline ngx_int_t

‌ngx_gettid(void)

{

    char  *sp;

    if (ngx_thread_stack_size == 0) {

        return 0;

    }

#if ( __i386__ )

    __asm__ volatile ("mov %%esp, %0" : "=q" (sp));

#elif ( __amd64__ )

    __asm__ volatile ("mov %%rsp, %0" : "=q" (sp));

#else

#error "rfork()ed threads are not supported on this platform"

#endif

    return (ngx_freebsd_kern_usrstack - sp) / ngx_thread_stack_size;

}

ngx_tid_t ngx_thread_self(void);

‌typedef ngx_uint_t               ngx_tls_key_t;

‌#define NGX_THREAD_KEYS_MAX      16

extern void    **ngx_tls;

ngx_err_t ngx_thread_key_create(ngx_tls_key_t *key);

‌#define ngx_thread_key_create_n  "the tls key creation"

ngx_err_t ngx_thread_set_tls(ngx_tls_key_t key, void *value);

‌#define ngx_thread_set_tls_n     "the tls key setting"

static void *

‌ngx_thread_get_tls(ngx_tls_key_t key)

{

    if (key >= NGX_THREAD_KEYS_MAX) {

        return NULL;

    }

    return ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()];

}

‌#define ngx_mutex_trylock(m)  ngx_mutex_dolock(m, 1)

‌#define ngx_mutex_lock(m)     (void) ngx_mutex_dolock(m, 0)

ngx_int_t ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try);

void ngx_mutex_unlock(ngx_mutex_t *m);

‌typedef int (*ngx_rfork_thread_func_pt)(void *arg);

#endif /* _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ */

src/os/unix/ngx_process.h - nginx-1.7.9

Data types defined

ngx_exec_ctx_t

ngx_pid_t

ngx_process_t

ngx_spawn_proc_pt

Macros defined

NGX_INVALID_PID

NGX_MAX_PROCESSES

NGX_PROCESS_DETACHED

NGX_PROCESS_JUST_RESPAWN

NGX_PROCESS_JUST_SPAWN

NGX_PROCESS_NORESPAWN

NGX_PROCESS_RESPAWN

_NGX_PROCESS_H_INCLUDED_

ngx_getpid

ngx_log_pid

ngx_sched_yield

ngx_sched_yield

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_PROCESS_H_INCLUDED_

‌#define _NGX_PROCESS_H_INCLUDED_

#include <ngx_setaffinity.h>

#include <ngx_setproctitle.h>

‌typedef pid_t       ngx_pid_t;

‌#define NGX_INVALID_PID  -1

‌typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data);

typedef struct {

    ngx_pid_t           pid;

    int                 status;

    ngx_socket_t        channel[2];

    ngx_spawn_proc_pt   proc;

    void               *data;

    char               *name;

    unsigned            respawn:1;

    unsigned            just_spawn:1;

    unsigned            detached:1;

    unsigned            exiting:1;

    unsigned            exited:1;

‌} ngx_process_t;

typedef struct {

    char         *path;

    char         *name;

    char *const  *argv;

    char *const  *envp;

‌} ngx_exec_ctx_t;

‌#define NGX_MAX_PROCESSES         1024

‌#define NGX_PROCESS_NORESPAWN     -1

‌#define NGX_PROCESS_JUST_SPAWN    -2

‌#define NGX_PROCESS_RESPAWN       -3

‌#define NGX_PROCESS_JUST_RESPAWN  -4

‌#define NGX_PROCESS_DETACHED      -5

‌#define ngx_getpid   getpid

#ifndef ngx_log_pid

‌#define ngx_log_pid  ngx_pid

#endif

ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,

    ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn);

ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);

ngx_int_t ngx_init_signals(ngx_log_t *log);

void ngx_debug_point(void);

#if (NGX_HAVE_SCHED_YIELD)

‌#define ngx_sched_yield()  sched_yield()

#else

‌#define ngx_sched_yield()  usleep(1)

#endif

extern int            ngx_argc;

extern char         **ngx_argv;

extern char         **ngx_os_argv;

extern ngx_pid_t      ngx_pid;

extern ngx_socket_t   ngx_channel;

extern ngx_int_t      ngx_process_slot;

extern ngx_int_t      ngx_last_process;

extern ngx_process_t  ngx_processes[NGX_MAX_PROCESSES];

#endif /* _NGX_PROCESS_H_INCLUDED_ */

src/os/unix/ngx_setaffinity.c - nginx-1.7.9

Functions defined

ngx_setaffinity

ngx_setaffinity

Source code

/*

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_CPUSET_SETAFFINITY)

#include <sys/cpuset.h>

void

‌ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log)

{

    cpuset_t    mask;

    ngx_uint_t  i;

    ngx_log_error(NGX_LOG_NOTICE, log, 0,

                  "cpuset_setaffinity(0x%08Xl)", cpu_affinity);

    CPU_ZERO(&mask);

    i = 0;

    do {

        if (cpu_affinity & 1) {

            CPU_SET(i, &mask);

        }

        i++;

        cpu_affinity >>= 1;

    } while (cpu_affinity);

    if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,

                           sizeof(cpuset_t), &mask) == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "cpuset_setaffinity() failed");

    }

}

#elif (NGX_HAVE_SCHED_SETAFFINITY)

void

‌ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log)

{

    cpu_set_t   mask;

    ngx_uint_t  i;

    ngx_log_error(NGX_LOG_NOTICE, log, 0,

                  "sched_setaffinity(0x%08Xl)", cpu_affinity);

    CPU_ZERO(&mask);

    i = 0;

    do {

        if (cpu_affinity & 1) {

            CPU_SET(i, &mask);

        }

        i++;

        cpu_affinity >>= 1;

    } while (cpu_affinity);

    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sched_setaffinity() failed");

    }

}

#endif

src/os/unix/ngx_setproctitle.h - nginx-1.7.9

Macros defined

NGX_SETPROCTITLE_PAD

NGX_SETPROCTITLE_PAD

NGX_SETPROCTITLE_USES_ENV

NGX_SETPROCTITLE_USES_ENV

_NGX_SETPROCTITLE_H_INCLUDED_

ngx_init_setproctitle

ngx_init_setproctitle

ngx_setproctitle

ngx_setproctitle

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SETPROCTITLE_H_INCLUDED_

‌#define _NGX_SETPROCTITLE_H_INCLUDED_

#if (NGX_HAVE_SETPROCTITLE)

/* FreeBSD, NetBSD, OpenBSD */

‌#define ngx_init_setproctitle(log) NGX_OK

‌#define ngx_setproctitle(title)    setproctitle("%s", title)

#else /* !NGX_HAVE_SETPROCTITLE */

#if !defined NGX_SETPROCTITLE_USES_ENV

#if (NGX_SOLARIS)

‌#define NGX_SETPROCTITLE_USES_ENV  1

‌#define NGX_SETPROCTITLE_PAD       ' '

ngx_int_t ngx_init_setproctitle(ngx_log_t *log);

void ngx_setproctitle(char *title);

#elif (NGX_LINUX) || (NGX_DARWIN)

‌#define NGX_SETPROCTITLE_USES_ENV  1

‌#define NGX_SETPROCTITLE_PAD       '\0'

ngx_int_t ngx_init_setproctitle(ngx_log_t *log);

void ngx_setproctitle(char *title);

#else

‌#define ngx_init_setproctitle(log) NGX_OK

‌#define ngx_setproctitle(title)

#endif /* OSes */

#endif /* NGX_SETPROCTITLE_USES_ENV */

#endif /* NGX_HAVE_SETPROCTITLE */

#endif /* _NGX_SETPROCTITLE_H_INCLUDED_ */

src/os/unix/ngx_socket.h - nginx-1.7.9

Data types defined

ngx_socket_t

Macros defined

NGX_WRITE_SHUTDOWN

_NGX_SOCKET_H_INCLUDED_

ngx_blocking

ngx_blocking_n

ngx_blocking_n

ngx_close_socket

ngx_close_socket_n

ngx_nonblocking

ngx_nonblocking_n

ngx_nonblocking_n

ngx_shutdown_socket

ngx_shutdown_socket_n

ngx_socket

ngx_socket_n

ngx_tcp_nopush_n

ngx_tcp_nopush_n

ngx_tcp_push_n

ngx_tcp_push_n

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SOCKET_H_INCLUDED_

‌#define _NGX_SOCKET_H_INCLUDED_

#include <ngx_config.h>

‌#define NGX_WRITE_SHUTDOWN SHUT_WR

‌typedef int  ngx_socket_t;

‌#define ngx_socket          socket

‌#define ngx_socket_n        "socket()"

#if (NGX_HAVE_FIONBIO)

int ngx_nonblocking(ngx_socket_t s);

int ngx_blocking(ngx_socket_t s);

‌#define ngx_nonblocking_n   "ioctl(FIONBIO)"

‌#define ngx_blocking_n      "ioctl(!FIONBIO)"

#else

‌#define ngx_nonblocking(s)  fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)

‌#define ngx_nonblocking_n   "fcntl(O_NONBLOCK)"

‌#define ngx_blocking(s)     fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)

‌#define ngx_blocking_n      "fcntl(!O_NONBLOCK)"

#endif

int ngx_tcp_nopush(ngx_socket_t s);

int ngx_tcp_push(ngx_socket_t s);

#if (NGX_LINUX)

‌#define ngx_tcp_nopush_n   "setsockopt(TCP_CORK)"

‌#define ngx_tcp_push_n     "setsockopt(!TCP_CORK)"

#else

‌#define ngx_tcp_nopush_n   "setsockopt(TCP_NOPUSH)"

‌#define ngx_tcp_push_n     "setsockopt(!TCP_NOPUSH)"

#endif

‌#define ngx_shutdown_socket    shutdown

‌#define ngx_shutdown_socket_n  "shutdown()"

‌#define ngx_close_socket    close

‌#define ngx_close_socket_n  "close() socket"

#endif /* _NGX_SOCKET_H_INCLUDED_ */

src/os/unix/ngx_socket.c - nginx-1.7.9

Functions defined

ngx_blocking

ngx_nonblocking

ngx_tcp_nopush

ngx_tcp_nopush

ngx_tcp_nopush

ngx_tcp_push

ngx_tcp_push

ngx_tcp_push

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* ioctl(FIONBIO) sets a non-blocking mode with the single syscall

* while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state

* using fcntl(F_GETFL).

*

* ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2

* and Solaris 7.

*

* ioctl() in Linux 2.4 and 2.6 uses BKL, however, fcntl(F_SETFL) uses it too.

*/

#if (NGX_HAVE_FIONBIO)

int

‌ngx_nonblocking(ngx_socket_t s)

{

    int  nb;

    nb = 1;

    return ioctl(s, FIONBIO, &nb);

}

int

‌ngx_blocking(ngx_socket_t s)

{

    int  nb;

    nb = 0;

    return ioctl(s, FIONBIO, &nb);

}

#endif

#if (NGX_FREEBSD)

int

‌ngx_tcp_nopush(ngx_socket_t s)

{

    int  tcp_nopush;

    tcp_nopush = 1;

    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,

                      (const void *) &tcp_nopush, sizeof(int));

}

int

‌ngx_tcp_push(ngx_socket_t s)

{

    int  tcp_nopush;

    tcp_nopush = 0;

    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,

                      (const void *) &tcp_nopush, sizeof(int));

}

#elif (NGX_LINUX)

int

‌ngx_tcp_nopush(ngx_socket_t s)

{

    int  cork;

    cork = 1;

    return setsockopt(s, IPPROTO_TCP, TCP_CORK,

                      (const void *) &cork, sizeof(int));

}

int

‌ngx_tcp_push(ngx_socket_t s)

{

    int  cork;

    cork = 0;

    return setsockopt(s, IPPROTO_TCP, TCP_CORK,

                      (const void *) &cork, sizeof(int));

}

#else

int

‌ngx_tcp_nopush(ngx_socket_t s)

{

    return 0;

}

int

‌ngx_tcp_push(ngx_socket_t s)

{

    return 0;

}

#endif

src/os/unix/ngx_process_cycle.c - nginx-1.7.9

Global variables defined

master_process

ngx_cache_loader_ctx

ngx_cache_manager_ctx

ngx_change_binary

ngx_daemonized

ngx_debug_quit

ngx_exit_cycle

ngx_exit_log

ngx_exit_log_file

ngx_exiting

ngx_inherited

ngx_new_binary

ngx_noaccept

ngx_noaccepting

ngx_pid

ngx_process

ngx_quit

ngx_reap

ngx_reconfigure

ngx_reopen

ngx_restart

ngx_sigalrm

ngx_sigio

ngx_terminate

ngx_threaded

ngx_threads

ngx_threads_n

Functions defined

ngx_cache_loader_process_handler

ngx_cache_manager_process_cycle

ngx_cache_manager_process_handler

ngx_channel_handler

ngx_master_process_cycle

ngx_master_process_exit

ngx_pass_open_channel

ngx_reap_children

ngx_signal_worker_processes

ngx_single_process_cycle

ngx_start_cache_manager_processes

ngx_start_worker_processes

ngx_wakeup_worker_threads

ngx_worker_process_cycle

ngx_worker_process_exit

ngx_worker_process_init

ngx_worker_thread_cycle

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_channel.h>

static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,

    ngx_int_t type);

static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle,

    ngx_uint_t respawn);

static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch);

static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);

static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);

static void ngx_master_process_exit(ngx_cycle_t *cycle);

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);

static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker);

static void ngx_worker_process_exit(ngx_cycle_t *cycle);

static void ngx_channel_handler(ngx_event_t *ev);

#if (NGX_THREADS)

static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle);

static ngx_thread_value_t ngx_worker_thread_cycle(void *data);

#endif

static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);

static void ngx_cache_manager_process_handler(ngx_event_t *ev);

static void ngx_cache_loader_process_handler(ngx_event_t *ev);

‌ngx_uint_t    ngx_process;

‌ngx_pid_t     ngx_pid;

‌ngx_uint_t    ngx_threaded;

‌sig_atomic_t  ngx_reap;

‌sig_atomic_t  ngx_sigio;

‌sig_atomic_t  ngx_sigalrm;

‌sig_atomic_t  ngx_terminate;

‌sig_atomic_t  ngx_quit;

‌sig_atomic_t  ngx_debug_quit;

‌ngx_uint_t    ngx_exiting;

‌sig_atomic_t  ngx_reconfigure;

‌sig_atomic_t  ngx_reopen;

‌sig_atomic_t  ngx_change_binary;

‌ngx_pid_t     ngx_new_binary;

‌ngx_uint_t    ngx_inherited;

‌ngx_uint_t    ngx_daemonized;

‌sig_atomic_t  ngx_noaccept;

‌ngx_uint_t    ngx_noaccepting;

‌ngx_uint_t    ngx_restart;

#if (NGX_THREADS)

‌volatile ngx_thread_t  ngx_threads[NGX_MAX_THREADS];

‌ngx_int_t              ngx_threads_n;

#endif

‌static u_char  master_process[] = "master process";

‌static ngx_cache_manager_ctx_t  ngx_cache_manager_ctx = {

    ngx_cache_manager_process_handler, "cache manager process", 0

};

‌static ngx_cache_manager_ctx_t  ngx_cache_loader_ctx = {

    ngx_cache_loader_process_handler, "cache loader process", 60000

};

‌static ngx_cycle_t      ngx_exit_cycle;

‌static ngx_log_t        ngx_exit_log;

‌static ngx_open_file_t  ngx_exit_log_file;

void

‌ngx_master_process_cycle(ngx_cycle_t *cycle)

{

    char              *title;

    u_char            *p;

    size_t             size;

    ngx_int_t          i;

    ngx_uint_t         n, sigio;

    sigset_t           set;

    struct itimerval   itv;

    ngx_uint_t         live;

    ngx_msec_t         delay;

    ngx_listening_t   *ls;

    ngx_core_conf_t   *ccf;

    sigemptyset(&set);

    sigaddset(&set, SIGCHLD);

    sigaddset(&set, SIGALRM);

    sigaddset(&set, SIGIO);

    sigaddset(&set, SIGINT);

    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "sigprocmask() failed");

    }

    sigemptyset(&set);

    size = sizeof(master_process);

    for (i = 0; i < ngx_argc; i++) {

        size += ngx_strlen(ngx_argv[i]) + 1;

    }

    title = ngx_pnalloc(cycle->pool, size);

    if (title == NULL) {

        /* fatal */

        exit(2);

    }

    p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);

    for (i = 0; i < ngx_argc; i++) {

        *p++ = ' ';

        p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);

    }

    ngx_setproctitle(title);

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_start_worker_processes(cycle, ccf->worker_processes,

                               NGX_PROCESS_RESPAWN);

    ngx_start_cache_manager_processes(cycle, 0);

    ngx_new_binary = 0;

    delay = 0;

    sigio = 0;

    live = 1;

    for ( ;; ) {

        if (delay) {

            if (ngx_sigalrm) {

                sigio = 0;

                delay *= 2;

                ngx_sigalrm = 0;

            }

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "termination cycle: %d", delay);

            itv.it_interval.tv_sec = 0;

            itv.it_interval.tv_usec = 0;

            itv.it_value.tv_sec = delay / 1000;

            itv.it_value.tv_usec = (delay % 1000 ) * 1000;

            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                              "setitimer() failed");

            }

        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");

        sigsuspend(&set);

        ngx_time_update();

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "wake up, sigio %i", sigio);

        if (ngx_reap) {

            ngx_reap = 0;

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");

            live = ngx_reap_children(cycle);

        }

        if (!live && (ngx_terminate || ngx_quit)) {

            ngx_master_process_exit(cycle);

        }

        if (ngx_terminate) {

            if (delay == 0) {

                delay = 50;

            }

            if (sigio) {

                sigio--;

                continue;

            }

            sigio = ccf->worker_processes + 2 /* cache processes */;

            if (delay > 1000) {

                ngx_signal_worker_processes(cycle, SIGKILL);

            } else {

                ngx_signal_worker_processes(cycle,

                                       ngx_signal_value(NGX_TERMINATE_SIGNAL));

            }

            continue;

        }

        if (ngx_quit) {

            ngx_signal_worker_processes(cycle,

                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

            ls = cycle->listening.elts;

            for (n = 0; n < cycle->listening.nelts; n++) {

                if (ngx_close_socket(ls[n].fd) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,

                                  ngx_close_socket_n " %V failed",

                                  &ls[n].addr_text);

                }

            }

            cycle->listening.nelts = 0;

            continue;

        }

        if (ngx_reconfigure) {

            ngx_reconfigure = 0;

            if (ngx_new_binary) {

                ngx_start_worker_processes(cycle, ccf->worker_processes,

                                           NGX_PROCESS_RESPAWN);

                ngx_start_cache_manager_processes(cycle, 0);

                ngx_noaccepting = 0;

                continue;

            }

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");

            cycle = ngx_init_cycle(cycle);

            if (cycle == NULL) {

                cycle = (ngx_cycle_t *) ngx_cycle;

                continue;

            }

            ngx_cycle = cycle;

            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,

                                                   ngx_core_module);

            ngx_start_worker_processes(cycle, ccf->worker_processes,

                                       NGX_PROCESS_JUST_RESPAWN);

            ngx_start_cache_manager_processes(cycle, 1);

            /* allow new processes to start */

            ngx_msleep(100);

            live = 1;

            ngx_signal_worker_processes(cycle,

                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

        }

        if (ngx_restart) {

            ngx_restart = 0;

            ngx_start_worker_processes(cycle, ccf->worker_processes,

                                       NGX_PROCESS_RESPAWN);

            ngx_start_cache_manager_processes(cycle, 0);

            live = 1;

        }

        if (ngx_reopen) {

            ngx_reopen = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");

            ngx_reopen_files(cycle, ccf->user);

            ngx_signal_worker_processes(cycle,

                                        ngx_signal_value(NGX_REOPEN_SIGNAL));

        }

        if (ngx_change_binary) {

            ngx_change_binary = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");

            ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);

        }

        if (ngx_noaccept) {

            ngx_noaccept = 0;

            ngx_noaccepting = 1;

            ngx_signal_worker_processes(cycle,

                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

        }

    }

}

void

‌ngx_single_process_cycle(ngx_cycle_t *cycle)

{

    ngx_uint_t  i;

    if (ngx_set_environment(cycle, NULL) == NULL) {

        /* fatal */

        exit(2);

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->init_process) {

            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {

                /* fatal */

                exit(2);

            }

        }

    }

    for ( ;; ) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);

        if (ngx_terminate || ngx_quit) {

            for (i = 0; ngx_modules[i]; i++) {

                if (ngx_modules[i]->exit_process) {

                    ngx_modules[i]->exit_process(cycle);

                }

            }

            ngx_master_process_exit(cycle);

        }

        if (ngx_reconfigure) {

            ngx_reconfigure = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");

            cycle = ngx_init_cycle(cycle);

            if (cycle == NULL) {

                cycle = (ngx_cycle_t *) ngx_cycle;

                continue;

            }

            ngx_cycle = cycle;

        }

        if (ngx_reopen) {

            ngx_reopen = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");

            ngx_reopen_files(cycle, (ngx_uid_t) -1);

        }

    }

}

static void

‌ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)

{

    ngx_int_t      i;

    ngx_channel_t  ch;

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");

    ngx_memzero(&ch, sizeof(ngx_channel_t));

    ch.command = NGX_CMD_OPEN_CHANNEL;

    for (i = 0; i < n; i++) {

        ngx_spawn_process(cycle, ngx_worker_process_cycle,

                          (void *) (intptr_t) i, "worker process", type);

        ch.pid = ngx_processes[ngx_process_slot].pid;

        ch.slot = ngx_process_slot;

        ch.fd = ngx_processes[ngx_process_slot].channel[0];

        ngx_pass_open_channel(cycle, &ch);

    }

}

static void

‌ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)

{

    ngx_uint_t       i, manager, loader;

    ngx_path_t     **path;

    ngx_channel_t    ch;

    manager = 0;

    loader = 0;

    path = ngx_cycle->paths.elts;

    for (i = 0; i < ngx_cycle->paths.nelts; i++) {

        if (path[i]->manager) {

            manager = 1;

        }

        if (path[i]->loader) {

            loader = 1;

        }

    }

    if (manager == 0) {

        return;

    }

    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,

                      &ngx_cache_manager_ctx, "cache manager process",

                      respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);

    ngx_memzero(&ch, sizeof(ngx_channel_t));

    ch.command = NGX_CMD_OPEN_CHANNEL;

    ch.pid = ngx_processes[ngx_process_slot].pid;

    ch.slot = ngx_process_slot;

    ch.fd = ngx_processes[ngx_process_slot].channel[0];

    ngx_pass_open_channel(cycle, &ch);

    if (loader == 0) {

        return;

    }

    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,

                      &ngx_cache_loader_ctx, "cache loader process",

                      respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);

    ch.command = NGX_CMD_OPEN_CHANNEL;

    ch.pid = ngx_processes[ngx_process_slot].pid;

    ch.slot = ngx_process_slot;

    ch.fd = ngx_processes[ngx_process_slot].channel[0];

    ngx_pass_open_channel(cycle, &ch);

}

static void

‌ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)

{

    ngx_int_t  i;

    for (i = 0; i < ngx_last_process; i++) {

        if (i == ngx_process_slot

            || ngx_processes[i].pid == -1

            || ngx_processes[i].channel[0] == -1)

        {

            continue;

        }

        ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                      "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",

                      ch->slot, ch->pid, ch->fd,

                      i, ngx_processes[i].pid,

                      ngx_processes[i].channel[0]);

        /* TODO: NGX_AGAIN */

        ngx_write_channel(ngx_processes[i].channel[0],

                          ch, sizeof(ngx_channel_t), cycle->log);

    }

}

static void

‌ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)

{

    ngx_int_t      i;

    ngx_err_t      err;

    ngx_channel_t  ch;

    ngx_memzero(&ch, sizeof(ngx_channel_t));

#if (NGX_BROKEN_SCM_RIGHTS)

    ch.command = 0;

#else

    switch (signo) {

    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):

        ch.command = NGX_CMD_QUIT;

        break;

    case ngx_signal_value(NGX_TERMINATE_SIGNAL):

        ch.command = NGX_CMD_TERMINATE;

        break;

    case ngx_signal_value(NGX_REOPEN_SIGNAL):

        ch.command = NGX_CMD_REOPEN;

        break;

    default:

        ch.command = 0;

    }

#endif

    ch.fd = -1;

    for (i = 0; i < ngx_last_process; i++) {

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "child: %d %P e:%d t:%d d:%d r:%d j:%d",

                       i,

                       ngx_processes[i].pid,

                       ngx_processes[i].exiting,

                       ngx_processes[i].exited,

                       ngx_processes[i].detached,

                       ngx_processes[i].respawn,

                       ngx_processes[i].just_spawn);

        if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {

            continue;

        }

        if (ngx_processes[i].just_spawn) {

            ngx_processes[i].just_spawn = 0;

            continue;

        }

        if (ngx_processes[i].exiting

            && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))

        {

            continue;

        }

        if (ch.command) {

            if (ngx_write_channel(ngx_processes[i].channel[0],

                                  &ch, sizeof(ngx_channel_t), cycle->log)

                == NGX_OK)

            {

                if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {

                    ngx_processes[i].exiting = 1;

                }

                continue;

            }

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                       "kill (%P, %d)", ngx_processes[i].pid, signo);

        if (kill(ngx_processes[i].pid, signo) == -1) {

            err = ngx_errno;

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "kill(%P, %d) failed", ngx_processes[i].pid, signo);

            if (err == NGX_ESRCH) {

                ngx_processes[i].exited = 1;

                ngx_processes[i].exiting = 0;

                ngx_reap = 1;

            }

            continue;

        }

        if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {

            ngx_processes[i].exiting = 1;

        }

    }

}

static ngx_uint_t

‌ngx_reap_children(ngx_cycle_t *cycle)

{

    ngx_int_t         i, n;

    ngx_uint_t        live;

    ngx_channel_t     ch;

    ngx_core_conf_t  *ccf;

    ngx_memzero(&ch, sizeof(ngx_channel_t));

    ch.command = NGX_CMD_CLOSE_CHANNEL;

    ch.fd = -1;

    live = 0;

    for (i = 0; i < ngx_last_process; i++) {

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "child: %d %P e:%d t:%d d:%d r:%d j:%d",

                       i,

                       ngx_processes[i].pid,

                       ngx_processes[i].exiting,

                       ngx_processes[i].exited,

                       ngx_processes[i].detached,

                       ngx_processes[i].respawn,

                       ngx_processes[i].just_spawn);

        if (ngx_processes[i].pid == -1) {

            continue;

        }

        if (ngx_processes[i].exited) {

            if (!ngx_processes[i].detached) {

                ngx_close_channel(ngx_processes[i].channel, cycle->log);

                ngx_processes[i].channel[0] = -1;

                ngx_processes[i].channel[1] = -1;

                ch.pid = ngx_processes[i].pid;

                ch.slot = i;

                for (n = 0; n < ngx_last_process; n++) {

                    if (ngx_processes[n].exited

                        || ngx_processes[n].pid == -1

                        || ngx_processes[n].channel[0] == -1)

                    {

                        continue;

                    }

                    ngx_log_debug3(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                                   "pass close channel s:%i pid:%P to:%P",

                                   ch.slot, ch.pid, ngx_processes[n].pid);

                    /* TODO: NGX_AGAIN */

                    ngx_write_channel(ngx_processes[n].channel[0],

                                      &ch, sizeof(ngx_channel_t), cycle->log);

                }

            }

            if (ngx_processes[i].respawn

                && !ngx_processes[i].exiting

                && !ngx_terminate

                && !ngx_quit)

            {

                if (ngx_spawn_process(cycle, ngx_processes[i].proc,

                                      ngx_processes[i].data,

                                      ngx_processes[i].name, i)

                    == NGX_INVALID_PID)

                {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                                  "could not respawn %s",

                                  ngx_processes[i].name);

                    continue;

                }

                ch.command = NGX_CMD_OPEN_CHANNEL;

                ch.pid = ngx_processes[ngx_process_slot].pid;

                ch.slot = ngx_process_slot;

                ch.fd = ngx_processes[ngx_process_slot].channel[0];

                ngx_pass_open_channel(cycle, &ch);

                live = 1;

                continue;

            }

            if (ngx_processes[i].pid == ngx_new_binary) {

                ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,

                                                       ngx_core_module);

                if (ngx_rename_file((char *) ccf->oldpid.data,

                                    (char *) ccf->pid.data)

                    == NGX_FILE_ERROR)

                {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                                  ngx_rename_file_n " %s back to %s failed "

                                  "after the new binary process \"%s\" exited",

                                  ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);

                }

                ngx_new_binary = 0;

                if (ngx_noaccepting) {

                    ngx_restart = 1;

                    ngx_noaccepting = 0;

                }

            }

            if (i == ngx_last_process - 1) {

                ngx_last_process--;

            } else {

                ngx_processes[i].pid = -1;

            }

        } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {

            live = 1;

        }

    }

    return live;

}

static void

‌ngx_master_process_exit(ngx_cycle_t *cycle)

{

    ngx_uint_t  i;

    ngx_delete_pidfile(cycle);

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit");

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->exit_master) {

            ngx_modules[i]->exit_master(cycle);

        }

    }

    ngx_close_listening_sockets(cycle);

    /*

     * Copy ngx_cycle->log related data to the special static exit cycle,

     * log, and log file structures enough to allow a signal handler to log.

     * The handler may be called when standard ngx_cycle->log allocated from

     * ngx_cycle->pool is already destroyed.

     */

    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);

    ngx_exit_log_file.fd = ngx_exit_log.file->fd;

    ngx_exit_log.file = &ngx_exit_log_file;

    ngx_exit_log.next = NULL;

    ngx_exit_log.writer = NULL;

    ngx_exit_cycle.log = &ngx_exit_log;

    ngx_exit_cycle.files = ngx_cycle->files;

    ngx_exit_cycle.files_n = ngx_cycle->files_n;

    ngx_cycle = &ngx_exit_cycle;

    ngx_destroy_pool(cycle->pool);

    exit(0);

}

static void

‌ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)

{

    ngx_int_t worker = (intptr_t) data;

    ngx_uint_t         i;

    ngx_connection_t  *c;

    ngx_process = NGX_PROCESS_WORKER;

    ngx_worker_process_init(cycle, worker);

    ngx_setproctitle("worker process");

#if (NGX_THREADS)

    {

    ngx_int_t         n;

    ngx_err_t         err;

    ngx_core_conf_t  *ccf;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ngx_threads_n) {

        if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)

            == NGX_ERROR)

        {

            /* fatal */

            exit(2);

        }

        err = ngx_thread_key_create(&ngx_core_tls_key);

        if (err != 0) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          ngx_thread_key_create_n " failed");

            /* fatal */

            exit(2);

        }

        for (n = 0; n < ngx_threads_n; n++) {

            ngx_threads[n].cv = ngx_cond_init(cycle->log);

            if (ngx_threads[n].cv == NULL) {

                /* fatal */

                exit(2);

            }

            if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid,

                                  ngx_worker_thread_cycle,

                                  (void *) &ngx_threads[n], cycle->log)

                != 0)

            {

                /* fatal */

                exit(2);

            }

        }

    }

    }

#endif

    for ( ;; ) {

        if (ngx_exiting) {

            c = cycle->connections;

            for (i = 0; i < cycle->connection_n; i++) {

                /* THREAD: lock */

                if (c[i].fd != -1 && c[i].idle) {

                    c[i].close = 1;

                    c[i].read->handler(c[i].read);

                }

            }

            ngx_event_cancel_timers();

            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)

            {

                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

                ngx_worker_process_exit(cycle);

            }

        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);

        if (ngx_terminate) {

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

            ngx_worker_process_exit(cycle);

        }

        if (ngx_quit) {

            ngx_quit = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,

                          "gracefully shutting down");

            ngx_setproctitle("worker process is shutting down");

            if (!ngx_exiting) {

                ngx_close_listening_sockets(cycle);

                ngx_exiting = 1;

            }

        }

        if (ngx_reopen) {

            ngx_reopen = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");

            ngx_reopen_files(cycle, -1);

        }

    }

}

static void

‌ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)

{

    sigset_t          set;

    uint64_t          cpu_affinity;

    ngx_int_t         n;

    ngx_uint_t        i;

    struct rlimit     rlmt;

    ngx_core_conf_t  *ccf;

    ngx_listening_t  *ls;

    if (ngx_set_environment(cycle, NULL) == NULL) {

        /* fatal */

        exit(2);

    }

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (worker >= 0 && ccf->priority != 0) {

        if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setpriority(%d) failed", ccf->priority);

        }

    }

    if (ccf->rlimit_nofile != NGX_CONF_UNSET) {

        rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;

        rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;

        if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setrlimit(RLIMIT_NOFILE, %i) failed",

                          ccf->rlimit_nofile);

        }

    }

    if (ccf->rlimit_core != NGX_CONF_UNSET) {

        rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;

        rlmt.rlim_max = (rlim_t) ccf->rlimit_core;

        if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setrlimit(RLIMIT_CORE, %O) failed",

                          ccf->rlimit_core);

        }

    }

#ifdef RLIMIT_SIGPENDING

    if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {

        rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;

        rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;

        if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setrlimit(RLIMIT_SIGPENDING, %i) failed",

                          ccf->rlimit_sigpending);

        }

    }

#endif

    if (geteuid() == 0) {

        if (setgid(ccf->group) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "setgid(%d) failed", ccf->group);

            /* fatal */

            exit(2);

        }

        if (initgroups(ccf->username, ccf->group) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "initgroups(%s, %d) failed",

                          ccf->username, ccf->group);

        }

        if (setuid(ccf->user) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "setuid(%d) failed", ccf->user);

            /* fatal */

            exit(2);

        }

    }

    if (worker >= 0) {

        cpu_affinity = ngx_get_cpu_affinity(worker);

        if (cpu_affinity) {

            ngx_setaffinity(cpu_affinity, cycle->log);

        }

    }

#if (NGX_HAVE_PR_SET_DUMPABLE)

    /* allow coredump after setuid() in Linux 2.4.x */

    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "prctl(PR_SET_DUMPABLE) failed");

    }

#endif

    if (ccf->working_directory.len) {

        if (chdir((char *) ccf->working_directory.data) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "chdir(\"%s\") failed", ccf->working_directory.data);

            /* fatal */

            exit(2);

        }

    }

    sigemptyset(&set);

    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "sigprocmask() failed");

    }

    srandom((ngx_pid << 16) ^ ngx_time());

    /*

     * disable deleting previous events for the listening sockets because

     * in the worker processes there are no events at all at this point

     */

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        ls[i].previous = NULL;

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->init_process) {

            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {

                /* fatal */

                exit(2);

            }

        }

    }

    for (n = 0; n < ngx_last_process; n++) {

        if (ngx_processes[n].pid == -1) {

            continue;

        }

        if (n == ngx_process_slot) {

            continue;

        }

        if (ngx_processes[n].channel[1] == -1) {

            continue;

        }

        if (close(ngx_processes[n].channel[1]) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "close() channel failed");

        }

    }

    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "close() channel failed");

    }

#if 0

    ngx_last_process = 0;

#endif

    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,

                              ngx_channel_handler)

        == NGX_ERROR)

    {

        /* fatal */

        exit(2);

    }

}

static void

‌ngx_worker_process_exit(ngx_cycle_t *cycle)

{

    ngx_uint_t         i;

    ngx_connection_t  *c;

#if (NGX_THREADS)

    ngx_terminate = 1;

    ngx_wakeup_worker_threads(cycle);

#endif

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->exit_process) {

            ngx_modules[i]->exit_process(cycle);

        }

    }

    if (ngx_exiting) {

        c = cycle->connections;

        for (i = 0; i < cycle->connection_n; i++) {

            if (c[i].fd != -1

                && c[i].read

                && !c[i].read->accept

                && !c[i].read->channel

                && !c[i].read->resolver)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                              "*%uA open socket #%d left in connection %ui",

                              c[i].number, c[i].fd, i);

                ngx_debug_quit = 1;

            }

        }

        if (ngx_debug_quit) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting");

            ngx_debug_point();

        }

    }

    /*

     * Copy ngx_cycle->log related data to the special static exit cycle,

     * log, and log file structures enough to allow a signal handler to log.

     * The handler may be called when standard ngx_cycle->log allocated from

     * ngx_cycle->pool is already destroyed.

     */

    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);

    ngx_exit_log_file.fd = ngx_exit_log.file->fd;

    ngx_exit_log.file = &ngx_exit_log_file;

    ngx_exit_log.next = NULL;

    ngx_exit_log.writer = NULL;

    ngx_exit_cycle.log = &ngx_exit_log;

    ngx_exit_cycle.files = ngx_cycle->files;

    ngx_exit_cycle.files_n = ngx_cycle->files_n;

    ngx_cycle = &ngx_exit_cycle;

    ngx_destroy_pool(cycle->pool);

    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit");

    exit(0);

}

static void

‌ngx_channel_handler(ngx_event_t *ev)

{

    ngx_int_t          n;

    ngx_channel_t      ch;

    ngx_connection_t  *c;

    if (ev->timedout) {

        ev->timedout = 0;

        return;

    }

    c = ev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler");

    for ( ;; ) {

        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);

        if (n == NGX_ERROR) {

            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {

                ngx_del_conn(c, 0);

            }

            ngx_close_connection(c);

            return;

        }

        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {

            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return;

            }

        }

        if (n == NGX_AGAIN) {

            return;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,

                       "channel command: %d", ch.command);

        switch (ch.command) {

        case NGX_CMD_QUIT:

            ngx_quit = 1;

            break;

        case NGX_CMD_TERMINATE:

            ngx_terminate = 1;

            break;

        case NGX_CMD_REOPEN:

            ngx_reopen = 1;

            break;

        case NGX_CMD_OPEN_CHANNEL:

            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,

                           "get channel s:%i pid:%P fd:%d",

                           ch.slot, ch.pid, ch.fd);

            ngx_processes[ch.slot].pid = ch.pid;

            ngx_processes[ch.slot].channel[0] = ch.fd;

            break;

        case NGX_CMD_CLOSE_CHANNEL:

            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,

                           "close channel s:%i pid:%P our:%P fd:%d",

                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,

                           ngx_processes[ch.slot].channel[0]);

            if (close(ngx_processes[ch.slot].channel[0]) == -1) {

                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                              "close() channel failed");

            }

            ngx_processes[ch.slot].channel[0] = -1;

            break;

        }

    }

}

#if (NGX_THREADS)

static void

‌ngx_wakeup_worker_threads(ngx_cycle_t *cycle)

{

    ngx_int_t   i;

    ngx_uint_t  live;

    for ( ;; ) {

        live = 0;

        for (i = 0; i < ngx_threads_n; i++) {

            if (ngx_threads[i].state < NGX_THREAD_EXIT) {

                if (ngx_cond_signal(ngx_threads[i].cv) == NGX_ERROR) {

                    ngx_threads[i].state = NGX_THREAD_DONE;

                } else {

                    live = 1;

                }

            }

            if (ngx_threads[i].state == NGX_THREAD_EXIT) {

                ngx_thread_join(ngx_threads[i].tid, NULL);

                ngx_threads[i].state = NGX_THREAD_DONE;

            }

        }

        if (live == 0) {

            ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                           "all worker threads are joined");

            /* STUB */

            ngx_done_events(cycle);

            return;

        }

        ngx_sched_yield();

    }

}

static ngx_thread_value_t

‌ngx_worker_thread_cycle(void *data)

{

    ngx_thread_t  *thr = data;

    sigset_t          set;

    ngx_err_t         err;

    ngx_core_tls_t   *tls;

    ngx_cycle_t      *cycle;

    cycle = (ngx_cycle_t *) ngx_cycle;

    sigemptyset(&set);

    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));

    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

    err = ngx_thread_sigmask(SIG_BLOCK, &set, NULL);

    if (err) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                      ngx_thread_sigmask_n " failed");

        return (ngx_thread_value_t) 1;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                   "thread " NGX_TID_T_FMT " started", ngx_thread_self());

    ngx_setthrtitle("worker thread");

    tls = ngx_calloc(sizeof(ngx_core_tls_t), cycle->log);

    if (tls == NULL) {

        return (ngx_thread_value_t) 1;

    }

    err = ngx_thread_set_tls(ngx_core_tls_key, tls);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                      ngx_thread_set_tls_n " failed");

        return (ngx_thread_value_t) 1;

    }

    for ( ;; ) {

        thr->state = NGX_THREAD_FREE;

#if 0

        if (ngx_cond_wait(thr->cv, ngx_posted_events_mutex) == NGX_ERROR) {

            return (ngx_thread_value_t) 1;

        }

#endif

        if (ngx_terminate) {

            thr->state = NGX_THREAD_EXIT;

            ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                           "thread " NGX_TID_T_FMT " is done",

                           ngx_thread_self());

            return (ngx_thread_value_t) 0;

        }

        thr->state = NGX_THREAD_BUSY;

#if 0

        if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) {

            return (ngx_thread_value_t) 1;

        }

        if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) {

            return (ngx_thread_value_t) 1;

        }

#endif

        if (ngx_process_changes) {

            if (ngx_process_changes(cycle, 1) == NGX_ERROR) {

                return (ngx_thread_value_t) 1;

            }

        }

    }

}

#endif

static void

‌ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)

{

    ngx_cache_manager_ctx_t *ctx = data;

    void         *ident[4];

    ngx_event_t   ev;

    /*

     * Set correct process type since closing listening Unix domain socket

     * in a master process also removes the Unix domain socket file.

     */

    ngx_process = NGX_PROCESS_HELPER;

    ngx_close_listening_sockets(cycle);

    /* Set a moderate number of connections for a helper process. */

    cycle->connection_n = 512;

    ngx_worker_process_init(cycle, -1);

    ngx_memzero(&ev, sizeof(ngx_event_t));

    ev.handler = ctx->handler;

    ev.data = ident;

    ev.log = cycle->log;

    ident[3] = (void *) -1;

    ngx_use_accept_mutex = 0;

    ngx_setproctitle(ctx->name);

    ngx_add_timer(&ev, ctx->delay);

    for ( ;; ) {

        if (ngx_terminate || ngx_quit) {

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

            exit(0);

        }

        if (ngx_reopen) {

            ngx_reopen = 0;

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");

            ngx_reopen_files(cycle, -1);

        }

        ngx_process_events_and_timers(cycle);

    }

}

static void

‌ngx_cache_manager_process_handler(ngx_event_t *ev)

{

    time_t        next, n;

    ngx_uint_t    i;

    ngx_path_t  **path;

    next = 60 * 60;

    path = ngx_cycle->paths.elts;

    for (i = 0; i < ngx_cycle->paths.nelts; i++) {

        if (path[i]->manager) {

            n = path[i]->manager(path[i]->data);

            next = (n <= next) ? n : next;

            ngx_time_update();

        }

    }

    if (next == 0) {

        next = 1;

    }

    ngx_add_timer(ev, next * 1000);

}

static void

‌ngx_cache_loader_process_handler(ngx_event_t *ev)

{

    ngx_uint_t     i;

    ngx_path_t   **path;

    ngx_cycle_t   *cycle;

    cycle = (ngx_cycle_t *) ngx_cycle;

    path = cycle->paths.elts;

    for (i = 0; i < cycle->paths.nelts; i++) {

        if (ngx_terminate || ngx_quit) {

            break;

        }

        if (path[i]->loader) {

            path[i]->loader(path[i]->data);

            ngx_time_update();

        }

    }

    exit(0);

}

src/os/unix/ngx_process.c - nginx-1.7.9

Global variables defined

ngx_argc

ngx_argv

ngx_channel

ngx_last_process

ngx_os_argv

ngx_process_slot

ngx_processes

signals

Data types defined

ngx_signal_t

Functions defined

ngx_debug_point

ngx_execute

ngx_execute_proc

ngx_init_signals

ngx_os_signal_process

ngx_process_get_status

ngx_signal_handler

ngx_spawn_process

ngx_unlock_mutexes

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_channel.h>

typedef struct {

    int     signo;

    char   *signame;

    char   *name;

    void  (*handler)(int signo);

‌} ngx_signal_t;

static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);

static void ngx_signal_handler(int signo);

static void ngx_process_get_status(void);

static void ngx_unlock_mutexes(ngx_pid_t pid);

‌int              ngx_argc;

‌char           **ngx_argv;

‌char           **ngx_os_argv;

‌ngx_int_t        ngx_process_slot;

‌ngx_socket_t     ngx_channel;

‌ngx_int_t        ngx_last_process;

‌ngx_process_t    ngx_processes[NGX_MAX_PROCESSES];

‌ngx_signal_t  signals[] = {

    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),

      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),

      "reload",

      ngx_signal_handler },

    { ngx_signal_value(NGX_REOPEN_SIGNAL),

      "SIG" ngx_value(NGX_REOPEN_SIGNAL),

      "reopen",

      ngx_signal_handler },

    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),

      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),

      "",

      ngx_signal_handler },

    { ngx_signal_value(NGX_TERMINATE_SIGNAL),

      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),

      "stop",

      ngx_signal_handler },

    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),

      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),

      "quit",

      ngx_signal_handler },

    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),

      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),

      "",

      ngx_signal_handler },

    { SIGALRM, "SIGALRM", "", ngx_signal_handler },

    { SIGINT, "SIGINT", "", ngx_signal_handler },

    { SIGIO, "SIGIO", "", ngx_signal_handler },

    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },

    { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },

    { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },

    { 0, NULL, "", NULL }

};

ngx_pid_t

‌ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,

    char *name, ngx_int_t respawn)

{

    u_long     on;

    ngx_pid_t  pid;

    ngx_int_t  s;

    if (respawn >= 0) {

        s = respawn;

    } else {

        for (s = 0; s < ngx_last_process; s++) {

            if (ngx_processes[s].pid == -1) {

                break;

            }

        }

        if (s == NGX_MAX_PROCESSES) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "no more than %d processes can be spawned",

                          NGX_MAX_PROCESSES);

            return NGX_INVALID_PID;

        }

    }

    if (respawn != NGX_PROCESS_DETACHED) {

        /* Solaris 9 still has no AF_LOCAL */

        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "socketpair() failed while spawning \"%s\"", name);

            return NGX_INVALID_PID;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                       "channel %d:%d",

                       ngx_processes[s].channel[0],

                       ngx_processes[s].channel[1]);

        if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          ngx_nonblocking_n " failed while spawning \"%s\"",

                          name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          ngx_nonblocking_n " failed while spawning \"%s\"",

                          name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        on = 1;

        if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "ioctl(FIOASYNC) failed while spawning \"%s\"", name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "fcntl(F_SETOWN) failed while spawning \"%s\"", name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",

                           name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",

                           name);

            ngx_close_channel(ngx_processes[s].channel, cycle->log);

            return NGX_INVALID_PID;

        }

        ngx_channel = ngx_processes[s].channel[1];

    } else {

        ngx_processes[s].channel[0] = -1;

        ngx_processes[s].channel[1] = -1;

    }

    ngx_process_slot = s;

    pid = fork();

    switch (pid) {

    case -1:

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "fork() failed while spawning \"%s\"", name);

        ngx_close_channel(ngx_processes[s].channel, cycle->log);

        return NGX_INVALID_PID;

    case 0:

        ngx_pid = ngx_getpid();

        proc(cycle, data);

        break;

    default:

        break;

    }

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);

    ngx_processes[s].pid = pid;

    ngx_processes[s].exited = 0;

    if (respawn >= 0) {

        return pid;

    }

    ngx_processes[s].proc = proc;

    ngx_processes[s].data = data;

    ngx_processes[s].name = name;

    ngx_processes[s].exiting = 0;

    switch (respawn) {

    case NGX_PROCESS_NORESPAWN:

        ngx_processes[s].respawn = 0;

        ngx_processes[s].just_spawn = 0;

        ngx_processes[s].detached = 0;

        break;

    case NGX_PROCESS_JUST_SPAWN:

        ngx_processes[s].respawn = 0;

        ngx_processes[s].just_spawn = 1;

        ngx_processes[s].detached = 0;

        break;

    case NGX_PROCESS_RESPAWN:

        ngx_processes[s].respawn = 1;

        ngx_processes[s].just_spawn = 0;

        ngx_processes[s].detached = 0;

        break;

    case NGX_PROCESS_JUST_RESPAWN:

        ngx_processes[s].respawn = 1;

        ngx_processes[s].just_spawn = 1;

        ngx_processes[s].detached = 0;

        break;

    case NGX_PROCESS_DETACHED:

        ngx_processes[s].respawn = 0;

        ngx_processes[s].just_spawn = 0;

        ngx_processes[s].detached = 1;

        break;

    }

    if (s == ngx_last_process) {

        ngx_last_process++;

    }

    return pid;

}

ngx_pid_t

‌ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)

{

    return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,

                             NGX_PROCESS_DETACHED);

}

static void

‌ngx_execute_proc(ngx_cycle_t *cycle, void *data)

{

    ngx_exec_ctx_t  *ctx = data;

    if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "execve() failed while executing %s \"%s\"",

                      ctx->name, ctx->path);

    }

    exit(1);

}

ngx_int_t

‌ngx_init_signals(ngx_log_t *log)

{

    ngx_signal_t      *sig;

    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {

        ngx_memzero(&sa, sizeof(struct sigaction));

        sa.sa_handler = sig->handler;

        sigemptyset(&sa.sa_mask);

        if (sigaction(sig->signo, &sa, NULL) == -1) {

#if (NGX_VALGRIND)

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          "sigaction(%s) failed, ignored", sig->signame);

#else

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                          "sigaction(%s) failed", sig->signame);

            return NGX_ERROR;

#endif

        }

    }

    return NGX_OK;

}

void

‌ngx_signal_handler(int signo)

{

    char            *action;

    ngx_int_t        ignore;

    ngx_err_t        err;

    ngx_signal_t    *sig;

    ignore = 0;

    err = ngx_errno;

    for (sig = signals; sig->signo != 0; sig++) {

        if (sig->signo == signo) {

            break;

        }

    }

    ngx_time_sigsafe_update();

    action = "";

    switch (ngx_process) {

    case NGX_PROCESS_MASTER:

    case NGX_PROCESS_SINGLE:

        switch (signo) {

        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):

            ngx_quit = 1;

            action = ", shutting down";

            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):

        case SIGINT:

            ngx_terminate = 1;

            action = ", exiting";

            break;

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):

            if (ngx_daemonized) {

                ngx_noaccept = 1;

                action = ", stop accepting connections";

            }

            break;

        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):

            ngx_reconfigure = 1;

            action = ", reconfiguring";

            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):

            ngx_reopen = 1;

            action = ", reopening logs";

            break;

        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):

            if (getppid() > 1 || ngx_new_binary > 0) {

                /*

                 * Ignore the signal in the new binary if its parent is

                 * not the init process, i.e. the old binary's process

                 * is still running.  Or ignore the signal in the old binary's

                 * process if the new binary's process is already running.

                 */

                action = ", ignoring";

                ignore = 1;

                break;

            }

            ngx_change_binary = 1;

            action = ", changing binary";

            break;

        case SIGALRM:

            ngx_sigalrm = 1;

            break;

        case SIGIO:

            ngx_sigio = 1;

            break;

        case SIGCHLD:

            ngx_reap = 1;

            break;

        }

        break;

    case NGX_PROCESS_WORKER:

    case NGX_PROCESS_HELPER:

        switch (signo) {

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):

            if (!ngx_daemonized) {

                break;

            }

            ngx_debug_quit = 1;

        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):

            ngx_quit = 1;

            action = ", shutting down";

            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):

        case SIGINT:

            ngx_terminate = 1;

            action = ", exiting";

            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):

            ngx_reopen = 1;

            action = ", reopening logs";

            break;

        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):

        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):

        case SIGIO:

            action = ", ignoring";

            break;

        }

        break;

    }

    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,

                  "signal %d (%s) received%s", signo, sig->signame, action);

    if (ignore) {

        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,

                      "the changing binary signal is ignored: "

                      "you should shutdown or terminate "

                      "before either old or new binary's process");

    }

    if (signo == SIGCHLD) {

        ngx_process_get_status();

    }

    ngx_set_errno(err);

}

static void

‌ngx_process_get_status(void)

{

    int              status;

    char            *process;

    ngx_pid_t        pid;

    ngx_err_t        err;

    ngx_int_t        i;

    ngx_uint_t       one;

    one = 0;

    for ( ;; ) {

        pid = waitpid(-1, &status, WNOHANG);

        if (pid == 0) {

            return;

        }

        if (pid == -1) {

            err = ngx_errno;

            if (err == NGX_EINTR) {

                continue;

            }

            if (err == NGX_ECHILD && one) {

                return;

            }

            /*

             * Solaris always calls the signal handler for each exited process

             * despite waitpid() may be already called for this process.

             *

             * When several processes exit at the same time FreeBSD may

             * erroneously call the signal handler for exited process

             * despite waitpid() may be already called for this process.

             */

            if (err == NGX_ECHILD) {

                ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err,

                              "waitpid() failed");

                return;

            }

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,

                          "waitpid() failed");

            return;

        }

        one = 1;

        process = "unknown process";

        for (i = 0; i < ngx_last_process; i++) {

            if (ngx_processes[i].pid == pid) {

                ngx_processes[i].status = status;

                ngx_processes[i].exited = 1;

                process = ngx_processes[i].name;

                break;

            }

        }

        if (WTERMSIG(status)) {

#ifdef WCOREDUMP

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "%s %P exited on signal %d%s",

                          process, pid, WTERMSIG(status),

                          WCOREDUMP(status) ? " (core dumped)" : "");

#else

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "%s %P exited on signal %d",

                          process, pid, WTERMSIG(status));

#endif

        } else {

            ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,

                          "%s %P exited with code %d",

                          process, pid, WEXITSTATUS(status));

        }

        if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "%s %P exited with fatal code %d "

                          "and cannot be respawned",

                          process, pid, WEXITSTATUS(status));

            ngx_processes[i].respawn = 0;

        }

        ngx_unlock_mutexes(pid);

    }

}

static void

‌ngx_unlock_mutexes(ngx_pid_t pid)

{

    ngx_uint_t        i;

    ngx_shm_zone_t   *shm_zone;

    ngx_list_part_t  *part;

    ngx_slab_pool_t  *sp;

    /*

     * unlock the accept mutex if the abnormally exited process

     * held it

     */

    if (ngx_accept_mutex_ptr) {

        (void) ngx_shmtx_force_unlock(&ngx_accept_mutex, pid);

    }

    /*

     * unlock shared memory mutexes if held by the abnormally exited

     * process

     */

    part = (ngx_list_part_t *) &ngx_cycle->shared_memory.part;

    shm_zone = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            shm_zone = part->elts;

            i = 0;

        }

        sp = (ngx_slab_pool_t *) shm_zone[i].shm.addr;

        if (ngx_shmtx_force_unlock(&sp->mutex, pid)) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "shared memory zone \"%V\" was locked by %P",

                          &shm_zone[i].shm.name, pid);

        }

    }

}

void

‌ngx_debug_point(void)

{

    ngx_core_conf_t  *ccf;

    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,

                                           ngx_core_module);

    switch (ccf->debug_points) {

    case NGX_DEBUG_POINTS_STOP:

        raise(SIGSTOP);

        break;

    case NGX_DEBUG_POINTS_ABORT:

        ngx_abort();

    }

}

ngx_int_t

‌ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)

{

    ngx_signal_t  *sig;

    for (sig = signals; sig->signo != 0; sig++) {

        if (ngx_strcmp(name, sig->name) == 0) {

            if (kill(pid, sig->signo) != -1) {

                return 0;

            }

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "kill(%P, %d) failed", pid, sig->signo);

        }

    }

    return 1;

}

src/os/unix/ngx_channel.c - nginx-1.7.9

Functions defined

ngx_add_channel_event

ngx_close_channel

ngx_read_channel

ngx_write_channel

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_channel.h>

ngx_int_t

‌ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,

    ngx_log_t *log)

{

    ssize_t             n;

    ngx_err_t           err;

    struct iovec        iov[1];

    struct msghdr       msg;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

    union {

        struct cmsghdr  cm;

        char            space[CMSG_SPACE(sizeof(int))];

    } cmsg;

    if (ch->fd == -1) {

        msg.msg_control = NULL;

        msg.msg_controllen = 0;

    } else {

        msg.msg_control = (caddr_t) &cmsg;

        msg.msg_controllen = sizeof(cmsg);

        ngx_memzero(&cmsg, sizeof(cmsg));

        cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));

        cmsg.cm.cmsg_level = SOL_SOCKET;

        cmsg.cm.cmsg_type = SCM_RIGHTS;

        /*

         * We have to use ngx_memcpy() instead of simple

         *   *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;

         * because some gcc 4.4 with -O2/3/s optimization issues the warning:

         *   dereferencing type-punned pointer will break strict-aliasing rules

         *

         * Fortunately, gcc with -O1 compiles this ngx_memcpy()

         * in the same simple assignment as in the code above

         */

        ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));

    }

    msg.msg_flags = 0;

#else

    if (ch->fd == -1) {

        msg.msg_accrights = NULL;

        msg.msg_accrightslen = 0;

    } else {

        msg.msg_accrights = (caddr_t) &ch->fd;

        msg.msg_accrightslen = sizeof(int);

    }

#endif

    iov[0].iov_base = (char *) ch;

    iov[0].iov_len = size;

    msg.msg_name = NULL;

    msg.msg_namelen = 0;

    msg.msg_iov = iov;

    msg.msg_iovlen = 1;

    n = sendmsg(s, &msg, 0);

    if (n == -1) {

        err = ngx_errno;

        if (err == NGX_EAGAIN) {

            return NGX_AGAIN;

        }

        ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, ngx_log_t *log)

{

    ssize_t             n;

    ngx_err_t           err;

    struct iovec        iov[1];

    struct msghdr       msg;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

    union {

        struct cmsghdr  cm;

        char            space[CMSG_SPACE(sizeof(int))];

    } cmsg;

#else

    int                 fd;

#endif

    iov[0].iov_base = (char *) ch;

    iov[0].iov_len = size;

    msg.msg_name = NULL;

    msg.msg_namelen = 0;

    msg.msg_iov = iov;

    msg.msg_iovlen = 1;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

    msg.msg_control = (caddr_t) &cmsg;

    msg.msg_controllen = sizeof(cmsg);

#else

    msg.msg_accrights = (caddr_t) &fd;

    msg.msg_accrightslen = sizeof(int);

#endif

    n = recvmsg(s, &msg, 0);

    if (n == -1) {

        err = ngx_errno;

        if (err == NGX_EAGAIN) {

            return NGX_AGAIN;

        }

        ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed");

        return NGX_ERROR;

    }

    if (n == 0) {

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "recvmsg() returned zero");

        return NGX_ERROR;

    }

    if ((size_t) n < sizeof(ngx_channel_t)) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "recvmsg() returned not enough data: %z", n);

        return NGX_ERROR;

    }

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

    if (ch->command == NGX_CMD_OPEN_CHANNEL) {

        if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {

            ngx_log_error(NGX_LOG_ALERT, log, 0,

                          "recvmsg() returned too small ancillary data");

            return NGX_ERROR;

        }

        if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)

        {

            ngx_log_error(NGX_LOG_ALERT, log, 0,

                          "recvmsg() returned invalid ancillary data "

                          "level %d or type %d",

                          cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);

            return NGX_ERROR;

        }

        /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */

        ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int));

    }

    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "recvmsg() truncated data");

    }

#else

    if (ch->command == NGX_CMD_OPEN_CHANNEL) {

        if (msg.msg_accrightslen != sizeof(int)) {

            ngx_log_error(NGX_LOG_ALERT, log, 0,

                          "recvmsg() returned no ancillary data");

            return NGX_ERROR;

        }

        ch->fd = fd;

    }

#endif

    return n;

}

ngx_int_t

‌ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,

    ngx_event_handler_pt handler)

{

    ngx_event_t       *ev, *rev, *wev;

    ngx_connection_t  *c;

    c = ngx_get_connection(fd, cycle->log);

    if (c == NULL) {

        return NGX_ERROR;

    }

    c->pool = cycle->pool;

    rev = c->read;

    wev = c->write;

    rev->log = cycle->log;

    wev->log = cycle->log;

    rev->channel = 1;

    wev->channel = 1;

    ev = (event == NGX_READ_EVENT) ? rev : wev;

    ev->handler = handler;

    if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {

        if (ngx_add_conn(c) == NGX_ERROR) {

            ngx_free_connection(c);

            return NGX_ERROR;

        }

    } else {

        if (ngx_add_event(ev, event, 0) == NGX_ERROR) {

            ngx_free_connection(c);

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

void

‌ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log)

{

    if (close(fd[0]) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");

    }

    if (close(fd[1]) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");

    }

}

src/os/unix/ngx_channel.h - nginx-1.7.9

Data types defined

ngx_channel_t

Macros defined

_NGX_CHANNEL_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CHANNEL_H_INCLUDED_

‌#define _NGX_CHANNEL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

typedef struct {

     ngx_uint_t  command;

     ngx_pid_t   pid;

     ngx_int_t   slot;

     ngx_fd_t    fd;

‌} ngx_channel_t;

ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,

    ngx_log_t *log);

ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,

    ngx_log_t *log);

ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd,

    ngx_int_t event, ngx_event_handler_pt handler);

void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log);

#endif /* _NGX_CHANNEL_H_INCLUDED_ */

src/os/unix/ngx_process_cycle.h - nginx-1.7.9

Data types defined

ngx_cache_manager_ctx_t

Macros defined

NGX_CMD_CLOSE_CHANNEL

NGX_CMD_OPEN_CHANNEL

NGX_CMD_QUIT

NGX_CMD_REOPEN

NGX_CMD_TERMINATE

NGX_PROCESS_HELPER

NGX_PROCESS_MASTER

NGX_PROCESS_SIGNALLER

NGX_PROCESS_SINGLE

NGX_PROCESS_WORKER

_NGX_PROCESS_CYCLE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_

‌#define _NGX_PROCESS_CYCLE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_CMD_OPEN_CHANNEL   1

‌#define NGX_CMD_CLOSE_CHANNEL  2

‌#define NGX_CMD_QUIT           3

‌#define NGX_CMD_TERMINATE      4

‌#define NGX_CMD_REOPEN         5

‌#define NGX_PROCESS_SINGLE     0

‌#define NGX_PROCESS_MASTER     1

‌#define NGX_PROCESS_SIGNALLER  2

‌#define NGX_PROCESS_WORKER     3

‌#define NGX_PROCESS_HELPER     4

typedef struct {

    ngx_event_handler_pt       handler;

    char                      *name;

    ngx_msec_t                 delay;

‌} ngx_cache_manager_ctx_t;

void ngx_master_process_cycle(ngx_cycle_t *cycle);

void ngx_single_process_cycle(ngx_cycle_t *cycle);

extern ngx_uint_t      ngx_process;

extern ngx_pid_t       ngx_pid;

extern ngx_pid_t       ngx_new_binary;

extern ngx_uint_t      ngx_inherited;

extern ngx_uint_t      ngx_daemonized;

extern ngx_uint_t      ngx_threaded;

extern ngx_uint_t      ngx_exiting;

extern sig_atomic_t    ngx_reap;

extern sig_atomic_t    ngx_sigio;

extern sig_atomic_t    ngx_sigalrm;

extern sig_atomic_t    ngx_quit;

extern sig_atomic_t    ngx_debug_quit;

extern sig_atomic_t    ngx_terminate;

extern sig_atomic_t    ngx_noaccept;

extern sig_atomic_t    ngx_reconfigure;

extern sig_atomic_t    ngx_reopen;

extern sig_atomic_t    ngx_change_binary;

#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */

src/core/ngx_connection.c - nginx-1.7.9

Global variables defined

ngx_io

Functions defined

ngx_close_connection

ngx_close_listening_sockets

ngx_configure_listening_sockets

ngx_connection_error

ngx_connection_local_sockaddr

ngx_create_listening

ngx_drain_connections

ngx_free_connection

ngx_get_connection

ngx_open_listening_sockets

ngx_reusable_connection

ngx_set_inherited_sockets

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌ngx_os_io_t  ngx_io;

static void ngx_drain_connections(void);

ngx_listening_t *

‌ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)

{

    size_t            len;

    ngx_listening_t  *ls;

    struct sockaddr  *sa;

    u_char            text[NGX_SOCKADDR_STRLEN];

    ls = ngx_array_push(&cf->cycle->listening);

    if (ls == NULL) {

        return NULL;

    }

    ngx_memzero(ls, sizeof(ngx_listening_t));

    sa = ngx_palloc(cf->pool, socklen);

    if (sa == NULL) {

        return NULL;

    }

    ngx_memcpy(sa, sockaddr, socklen);

    ls->sockaddr = sa;

    ls->socklen = socklen;

    len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1);

    ls->addr_text.len = len;

    switch (ls->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

         ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;

         break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

         ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;

         len++;

         break;

#endif

    case AF_INET:

         ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;

         break;

    default:

         ls->addr_text_max_len = NGX_SOCKADDR_STRLEN;

         break;

    }

    ls->addr_text.data = ngx_pnalloc(cf->pool, len);

    if (ls->addr_text.data == NULL) {

        return NULL;

    }

    ngx_memcpy(ls->addr_text.data, text, len);

    ls->fd = (ngx_socket_t) -1;

    ls->type = SOCK_STREAM;

    ls->backlog = NGX_LISTEN_BACKLOG;

    ls->rcvbuf = -1;

    ls->sndbuf = -1;

#if (NGX_HAVE_SETFIB)

    ls->setfib = -1;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

    ls->fastopen = -1;

#endif

    return ls;

}

ngx_int_t

‌ngx_set_inherited_sockets(ngx_cycle_t *cycle)

{

    size_t                     len;

    ngx_uint_t                 i;

    ngx_listening_t           *ls;

    socklen_t                  olen;

#if (NGX_HAVE_DEFERRED_ACCEPT || NGX_HAVE_TCP_FASTOPEN)

    ngx_err_t                  err;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

    struct accept_filter_arg   af;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

    int                        timeout;

#endif

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN);

        if (ls[i].sockaddr == NULL) {

            return NGX_ERROR;

        }

        ls[i].socklen = NGX_SOCKADDRLEN;

        if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {

            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,

                          "getsockname() of the inherited "

                          "socket #%d failed", ls[i].fd);

            ls[i].ignore = 1;

            continue;

        }

        switch (ls[i].sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

             ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;

             len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;

             break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        case AF_UNIX:

             ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;

             len = NGX_UNIX_ADDRSTRLEN;

             break;

#endif

        case AF_INET:

             ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;

             len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;

             break;

        default:

            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,

                          "the inherited socket #%d has "

                          "an unsupported protocol family", ls[i].fd);

            ls[i].ignore = 1;

            continue;

        }

        ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);

        if (ls[i].addr_text.data == NULL) {

            return NGX_ERROR;

        }

        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].socklen,

                            ls[i].addr_text.data, len, 1);

        if (len == 0) {

            return NGX_ERROR;

        }

        ls[i].addr_text.len = len;

        ls[i].backlog = NGX_LISTEN_BACKLOG;

        olen = sizeof(int);

        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,

                       &olen)

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                          "getsockopt(SO_RCVBUF) %V failed, ignored",

                          &ls[i].addr_text);

            ls[i].rcvbuf = -1;

        }

        olen = sizeof(int);

        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,

                       &olen)

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                          "getsockopt(SO_SNDBUF) %V failed, ignored",

                          &ls[i].addr_text);

            ls[i].sndbuf = -1;

        }

#if 0

        /* SO_SETFIB is currently a set only option */

#if (NGX_HAVE_SETFIB)

        olen = sizeof(int);

        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,

                       (void *) &ls[i].setfib, &olen)

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                          "getsockopt(SO_SETFIB) %V failed, ignored",

                          &ls[i].addr_text);

            ls[i].setfib = -1;

        }

#endif

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

        olen = sizeof(int);

        if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN,

                       (void *) &ls[i].fastopen, &olen)

            == -1)

        {

            err = ngx_socket_errno;

            if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {

                ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,

                              "getsockopt(TCP_FASTOPEN) %V failed, ignored",

                              &ls[i].addr_text);

            }

            ls[i].fastopen = -1;

        }

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

        ngx_memzero(&af, sizeof(struct accept_filter_arg));

        olen = sizeof(struct accept_filter_arg);

        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)

            == -1)

        {

            err = ngx_socket_errno;

            if (err == NGX_EINVAL) {

                continue;

            }

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,

                          "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored",

                          &ls[i].addr_text);

            continue;

        }

        if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') {

            continue;

        }

        ls[i].accept_filter = ngx_palloc(cycle->pool, 16);

        if (ls[i].accept_filter == NULL) {

            return NGX_ERROR;

        }

        (void) ngx_cpystrn((u_char *) ls[i].accept_filter,

                           (u_char *) af.af_name, 16);

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

        timeout = 0;

        olen = sizeof(int);

        if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)

            == -1)

        {

            err = ngx_socket_errno;

            if (err == NGX_EOPNOTSUPP) {

                continue;

            }

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,

                          "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored",

                          &ls[i].addr_text);

            continue;

        }

        if (olen < sizeof(int) || timeout == 0) {

            continue;

        }

        ls[i].deferred_accept = 1;

#endif

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_open_listening_sockets(ngx_cycle_t *cycle)

{

    int               reuseaddr;

    ngx_uint_t        i, tries, failed;

    ngx_err_t         err;

    ngx_log_t        *log;

    ngx_socket_t      s;

    ngx_listening_t  *ls;

    reuseaddr = 1;

#if (NGX_SUPPRESS_WARN)

    failed = 0;

#endif

    log = cycle->log;

    /* TODO: configurable try number */

    for (tries = 5; tries; tries--) {

        failed = 0;

        /* for each listening socket */

        ls = cycle->listening.elts;

        for (i = 0; i < cycle->listening.nelts; i++) {

            if (ls[i].ignore) {

                continue;

            }

            if (ls[i].fd != (ngx_socket_t) -1) {

                continue;

            }

            if (ls[i].inherited) {

                /* TODO: close on exit */

                /* TODO: nonblocking */

                /* TODO: deferred accept */

                continue;

            }

            s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);

            if (s == (ngx_socket_t) -1) {

                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                              ngx_socket_n " %V failed", &ls[i].addr_text);

                return NGX_ERROR;

            }

            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,

                           (const void *) &reuseaddr, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                              "setsockopt(SO_REUSEADDR) %V failed",

                              &ls[i].addr_text);

                if (ngx_close_socket(s) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                  ngx_close_socket_n " %V failed",

                                  &ls[i].addr_text);

                }

                return NGX_ERROR;

            }

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

            if (ls[i].sockaddr->sa_family == AF_INET6) {

                int  ipv6only;

                ipv6only = ls[i].ipv6only;

                if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,

                               (const void *) &ipv6only, sizeof(int))

                    == -1)

                {

                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                  "setsockopt(IPV6_V6ONLY) %V failed, ignored",

                                  &ls[i].addr_text);

                }

            }

#endif

            /* TODO: close on exit */

            if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {

                if (ngx_nonblocking(s) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                  ngx_nonblocking_n " %V failed",

                                  &ls[i].addr_text);

                    if (ngx_close_socket(s) == -1) {

                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                      ngx_close_socket_n " %V failed",

                                      &ls[i].addr_text);

                    }

                    return NGX_ERROR;

                }

            }

            ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,

                           "bind() %V #%d ", &ls[i].addr_text, s);

            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {

                err = ngx_socket_errno;

                if (err != NGX_EADDRINUSE || !ngx_test_config) {

                    ngx_log_error(NGX_LOG_EMERG, log, err,

                                  "bind() to %V failed", &ls[i].addr_text);

                }

                if (ngx_close_socket(s) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                  ngx_close_socket_n " %V failed",

                                  &ls[i].addr_text);

                }

                if (err != NGX_EADDRINUSE) {

                    return NGX_ERROR;

                }

                if (!ngx_test_config) {

                    failed = 1;

                }

                continue;

            }

#if (NGX_HAVE_UNIX_DOMAIN)

            if (ls[i].sockaddr->sa_family == AF_UNIX) {

                mode_t   mode;

                u_char  *name;

                name = ls[i].addr_text.data + sizeof("unix:") - 1;

                mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);

                if (chmod((char *) name, mode) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                  "chmod() \"%s\" failed", name);

                }

                if (ngx_test_config) {

                    if (ngx_delete_file(name) == NGX_FILE_ERROR) {

                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                      ngx_delete_file_n " %s failed", name);

                    }

                }

            }

#endif

            if (listen(s, ls[i].backlog) == -1) {

                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                              "listen() to %V, backlog %d failed",

                              &ls[i].addr_text, ls[i].backlog);

                if (ngx_close_socket(s) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                                  ngx_close_socket_n " %V failed",

                                  &ls[i].addr_text);

                }

                return NGX_ERROR;

            }

            ls[i].listen = 1;

            ls[i].fd = s;

        }

        if (!failed) {

            break;

        }

        /* TODO: delay configurable */

        ngx_log_error(NGX_LOG_NOTICE, log, 0,

                      "try again to bind() after 500ms");

        ngx_msleep(500);

    }

    if (failed) {

        ngx_log_error(NGX_LOG_EMERG, log, 0, "still could not bind()");

        return NGX_ERROR;

    }

    return NGX_OK;

}

void

‌ngx_configure_listening_sockets(ngx_cycle_t *cycle)

{

    int                        value;

    ngx_uint_t                 i;

    ngx_listening_t           *ls;

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

    struct accept_filter_arg   af;

#endif

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        ls[i].log = *ls[i].logp;

        if (ls[i].rcvbuf != -1) {

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF,

                           (const void *) &ls[i].rcvbuf, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_RCVBUF, %d) %V failed, ignored",

                              ls[i].rcvbuf, &ls[i].addr_text);

            }

        }

        if (ls[i].sndbuf != -1) {

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF,

                           (const void *) &ls[i].sndbuf, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_SNDBUF, %d) %V failed, ignored",

                              ls[i].sndbuf, &ls[i].addr_text);

            }

        }

        if (ls[i].keepalive) {

            value = (ls[i].keepalive == 1) ? 1 : 0;

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE,

                           (const void *) &value, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_KEEPALIVE, %d) %V failed, ignored",

                              value, &ls[i].addr_text);

            }

        }

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

        if (ls[i].keepidle) {

            value = ls[i].keepidle;

#if (NGX_KEEPALIVE_FACTOR)

            value *= NGX_KEEPALIVE_FACTOR;

#endif

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE,

                           (const void *) &value, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored",

                              value, &ls[i].addr_text);

            }

        }

        if (ls[i].keepintvl) {

            value = ls[i].keepintvl;

#if (NGX_KEEPALIVE_FACTOR)

            value *= NGX_KEEPALIVE_FACTOR;

#endif

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL,

                           (const void *) &value, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                             "setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored",

                             value, &ls[i].addr_text);

            }

        }

        if (ls[i].keepcnt) {

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT,

                           (const void *) &ls[i].keepcnt, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(TCP_KEEPCNT, %d) %V failed, ignored",

                              ls[i].keepcnt, &ls[i].addr_text);

            }

        }

#endif

#if (NGX_HAVE_SETFIB)

        if (ls[i].setfib != -1) {

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,

                           (const void *) &ls[i].setfib, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_SETFIB, %d) %V failed, ignored",

                              ls[i].setfib, &ls[i].addr_text);

            }

        }

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

        if (ls[i].fastopen != -1) {

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN,

                           (const void *) &ls[i].fastopen, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(TCP_FASTOPEN, %d) %V failed, ignored",

                              ls[i].fastopen, &ls[i].addr_text);

            }

        }

#endif

#if 0

        if (1) {

            int tcp_nodelay = 1;

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_NODELAY,

                       (const void *) &tcp_nodelay, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(TCP_NODELAY) %V failed, ignored",

                              &ls[i].addr_text);

            }

        }

#endif

        if (ls[i].listen) {

            /* change backlog via listen() */

            if (listen(ls[i].fd, ls[i].backlog) == -1) {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "listen() to %V, backlog %d failed, ignored",

                              &ls[i].addr_text, ls[i].backlog);

            }

        }

        /*

         * setting deferred mode should be last operation on socket,

         * because code may prematurely continue cycle on failure

         */

#if (NGX_HAVE_DEFERRED_ACCEPT)

#ifdef SO_ACCEPTFILTER

        if (ls[i].delete_deferred) {

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_ACCEPTFILTER, NULL) "

                              "for %V failed, ignored",

                              &ls[i].addr_text);

                if (ls[i].accept_filter) {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                                  "could not change the accept filter "

                                  "to \"%s\" for %V, ignored",

                                  ls[i].accept_filter, &ls[i].addr_text);

                }

                continue;

            }

            ls[i].deferred_accept = 0;

        }

        if (ls[i].add_deferred) {

            ngx_memzero(&af, sizeof(struct accept_filter_arg));

            (void) ngx_cpystrn((u_char *) af.af_name,

                               (u_char *) ls[i].accept_filter, 16);

            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER,

                           &af, sizeof(struct accept_filter_arg))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(SO_ACCEPTFILTER, \"%s\") "

                              "for %V failed, ignored",

                              ls[i].accept_filter, &ls[i].addr_text);

                continue;

            }

            ls[i].deferred_accept = 1;

        }

#endif

#ifdef TCP_DEFER_ACCEPT

        if (ls[i].add_deferred || ls[i].delete_deferred) {

            if (ls[i].add_deferred) {

                /*

                 * There is no way to find out how long a connection was

                 * in queue (and a connection may bypass deferred queue at all

                 * if syncookies were used), hence we use 1 second timeout

                 * here.

                 */

                value = 1;

            } else {

                value = 0;

            }

            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT,

                           &value, sizeof(int))

                == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,

                              "setsockopt(TCP_DEFER_ACCEPT, %d) for %V failed, "

                              "ignored",

                              value, &ls[i].addr_text);

                continue;

            }

        }

        if (ls[i].add_deferred) {

            ls[i].deferred_accept = 1;

        }

#endif

#endif /* NGX_HAVE_DEFERRED_ACCEPT */

    }

    return;

}

void

‌ngx_close_listening_sockets(ngx_cycle_t *cycle)

{

    ngx_uint_t         i;

    ngx_listening_t   *ls;

    ngx_connection_t  *c;

    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {

        return;

    }

    ngx_accept_mutex_held = 0;

    ngx_use_accept_mutex = 0;

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        c = ls[i].connection;

        if (c) {

            if (c->read->active) {

                if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

                    ngx_del_conn(c, NGX_CLOSE_EVENT);

                } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {

                    /*

                     * it seems that Linux-2.6.x OpenVZ sends events

                     * for closed shared listening sockets unless

                     * the events was explicitly deleted

                     */

                    ngx_del_event(c->read, NGX_READ_EVENT, 0);

                } else {

                    ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);

                }

            }

            ngx_free_connection(c);

            c->fd = (ngx_socket_t) -1;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,

                       "close listening %V #%d ", &ls[i].addr_text, ls[i].fd);

        if (ngx_close_socket(ls[i].fd) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,

                          ngx_close_socket_n " %V failed", &ls[i].addr_text);

        }

#if (NGX_HAVE_UNIX_DOMAIN)

        if (ls[i].sockaddr->sa_family == AF_UNIX

            && ngx_process <= NGX_PROCESS_MASTER

            && ngx_new_binary == 0)

        {

            u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1;

            if (ngx_delete_file(name) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,

                              ngx_delete_file_n " %s failed", name);

            }

        }

#endif

        ls[i].fd = (ngx_socket_t) -1;

    }

    cycle->listening.nelts = 0;

}

ngx_connection_t *

‌ngx_get_connection(ngx_socket_t s, ngx_log_t *log)

{

    ngx_uint_t         instance;

    ngx_event_t       *rev, *wev;

    ngx_connection_t  *c;

    /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */

    if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "the new socket has number %d, "

                      "but only %ui files are available",

                      s, ngx_cycle->files_n);

        return NULL;

    }

    /* ngx_mutex_lock */

    c = ngx_cycle->free_connections;

    if (c == NULL) {

        ngx_drain_connections();

        c = ngx_cycle->free_connections;

    }

    if (c == NULL) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "%ui worker_connections are not enough",

                      ngx_cycle->connection_n);

        /* ngx_mutex_unlock */

        return NULL;

    }

    ngx_cycle->free_connections = c->data;

    ngx_cycle->free_connection_n--;

    /* ngx_mutex_unlock */

    if (ngx_cycle->files) {

        ngx_cycle->files[s] = c;

    }

    rev = c->read;

    wev = c->write;

    ngx_memzero(c, sizeof(ngx_connection_t));

    c->read = rev;

    c->write = wev;

    c->fd = s;

    c->log = log;

    instance = rev->instance;

    ngx_memzero(rev, sizeof(ngx_event_t));

    ngx_memzero(wev, sizeof(ngx_event_t));

    rev->instance = !instance;

    wev->instance = !instance;

    rev->index = NGX_INVALID_INDEX;

    wev->index = NGX_INVALID_INDEX;

    rev->data = c;

    wev->data = c;

    wev->write = 1;

    return c;

}

void

‌ngx_free_connection(ngx_connection_t *c)

{

    /* ngx_mutex_lock */

    c->data = ngx_cycle->free_connections;

    ngx_cycle->free_connections = c;

    ngx_cycle->free_connection_n++;

    /* ngx_mutex_unlock */

    if (ngx_cycle->files) {

        ngx_cycle->files[c->fd] = NULL;

    }

}

void

‌ngx_close_connection(ngx_connection_t *c)

{

    ngx_err_t     err;

    ngx_uint_t    log_error, level;

    ngx_socket_t  fd;

    if (c->fd == (ngx_socket_t) -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed");

        return;

    }

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    if (c->write->timer_set) {

        ngx_del_timer(c->write);

    }

    if (ngx_del_conn) {

        ngx_del_conn(c, NGX_CLOSE_EVENT);

    } else {

        if (c->read->active || c->read->disabled) {

            ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);

        }

        if (c->write->active || c->write->disabled) {

            ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);

        }

    }

#if (NGX_THREADS)

    /*

     * we have to clean the connection information before the closing

     * because another thread may reopen the same file descriptor

     * before we clean the connection

     */

    ngx_unlock(&c->lock);

#endif

    if (c->read->posted) {

        ngx_delete_posted_event(c->read);

    }

    if (c->write->posted) {

        ngx_delete_posted_event(c->write);

    }

    c->read->closed = 1;

    c->write->closed = 1;

    ngx_reusable_connection(c, 0);

    log_error = c->log_error;

    ngx_free_connection(c);

    fd = c->fd;

    c->fd = (ngx_socket_t) -1;

    if (ngx_close_socket(fd) == -1) {

        err = ngx_socket_errno;

        if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {

            switch (log_error) {

            case NGX_ERROR_INFO:

                level = NGX_LOG_INFO;

                break;

            case NGX_ERROR_ERR:

                level = NGX_LOG_ERR;

                break;

            default:

                level = NGX_LOG_CRIT;

            }

        } else {

            level = NGX_LOG_CRIT;

        }

        /* we use ngx_cycle->log because c->log was in c->pool */

        ngx_log_error(level, ngx_cycle->log, err,

                      ngx_close_socket_n " %d failed", fd);

    }

}

void

‌ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)

{

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,

                   "reusable connection: %ui", reusable);

    if (c->reusable) {

        ngx_queue_remove(&c->queue);

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);

#endif

    }

    c->reusable = reusable;

    if (reusable) {

        /* need cast as ngx_cycle is volatile */

        ngx_queue_insert_head(

            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);

#endif

    }

}

static void

‌ngx_drain_connections(void)

{

    ngx_int_t          i;

    ngx_queue_t       *q;

    ngx_connection_t  *c;

    for (i = 0; i < 32; i++) {

        if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {

            break;

        }

        q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);

        c = ngx_queue_data(q, ngx_connection_t, queue);

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,

                       "reusing connection");

        c->close = 1;

        c->read->handler(c->read);

    }

}

ngx_int_t

‌ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,

    ngx_uint_t port)

{

    socklen_t             len;

    ngx_uint_t            addr;

    u_char                sa[NGX_SOCKADDRLEN];

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    ngx_uint_t            i;

    struct sockaddr_in6  *sin6;

#endif

    if (c->local_socklen == 0) {

        return NGX_ERROR;

    }

    switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

        for (addr = 0, i = 0; addr == 0 && i < 16; i++) {

            addr |= sin6->sin6_addr.s6_addr[i];

        }

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        addr = 1;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) c->local_sockaddr;

        addr = sin->sin_addr.s_addr;

        break;

    }

    if (addr == 0) {

        len = NGX_SOCKADDRLEN;

        if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) {

            ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");

            return NGX_ERROR;

        }

        c->local_sockaddr = ngx_palloc(c->pool, len);

        if (c->local_sockaddr == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(c->local_sockaddr, &sa, len);

        c->local_socklen = len;

    }

    if (s == NULL) {

        return NGX_OK;

    }

    s->len = ngx_sock_ntop(c->local_sockaddr, c->local_socklen,

                           s->data, s->len, port);

    return NGX_OK;

}

ngx_int_t

‌ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)

{

    ngx_uint_t  level;

    /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */

    if ((err == NGX_ECONNRESET

#if (NGX_WIN32)

         || err == NGX_ECONNABORTED

#endif

        ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)

    {

        return 0;

    }

#if (NGX_SOLARIS)

    if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) {

        return 0;

    }

#endif

    if (err == 0

        || err == NGX_ECONNRESET

#if (NGX_WIN32)

        || err == NGX_ECONNABORTED

#else

        || err == NGX_EPIPE

#endif

        || err == NGX_ENOTCONN

        || err == NGX_ETIMEDOUT

        || err == NGX_ECONNREFUSED

        || err == NGX_ENETDOWN

        || err == NGX_ENETUNREACH

        || err == NGX_EHOSTDOWN

        || err == NGX_EHOSTUNREACH)

    {

        switch (c->log_error) {

        case NGX_ERROR_IGNORE_EINVAL:

        case NGX_ERROR_IGNORE_ECONNRESET:

        case NGX_ERROR_INFO:

            level = NGX_LOG_INFO;

            break;

        default:

            level = NGX_LOG_ERR;

        }

    } else {

        level = NGX_LOG_ALERT;

    }

    ngx_log_error(level, c->log, err, text);

    return NGX_ERROR;

}

src/core/ngx_connection.h - nginx-1.7.9

Data types defined

ngx_connection_log_error_e

ngx_connection_s

ngx_connection_tcp_nodelay_e

ngx_connection_tcp_nopush_e

ngx_listening_s

ngx_listening_t

Macros defined

NGX_LOWLEVEL_BUFFERED

NGX_SPDY_BUFFERED

NGX_SSL_BUFFERED

_NGX_CONNECTION_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CONNECTION_H_INCLUDED_

‌#define _NGX_CONNECTION_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef struct ngx_listening_s  ngx_listening_t;

‌struct ngx_listening_s {

    ngx_socket_t        fd;

    struct sockaddr    *sockaddr;

    socklen_t           socklen;    /* size of sockaddr */

    size_t              addr_text_max_len;

    ngx_str_t           addr_text;

    int                 type;

    int                 backlog;

    int                 rcvbuf;

    int                 sndbuf;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    int                 keepidle;

    int                 keepintvl;

    int                 keepcnt;

#endif

    /* handler of accepted connection */

    ngx_connection_handler_pt   handler;

    void               *servers;  /* array of ngx_http_in_addr_t, for example */

    ngx_log_t           log;

    ngx_log_t          *logp;

    size_t              pool_size;

    /* should be here because of the AcceptEx() preread */

    size_t              post_accept_buffer_size;

    /* should be here because of the deferred accept */

    ngx_msec_t          post_accept_timeout;

    ngx_listening_t    *previous;

    ngx_connection_t   *connection;

    unsigned            open:1;

    unsigned            remain:1;

    unsigned            ignore:1;

    unsigned            bound:1;       /* already bound */

    unsigned            inherited:1;   /* inherited from previous process */

    unsigned            nonblocking_accept:1;

    unsigned            listen:1;

    unsigned            nonblocking:1;

    unsigned            shared:1;    /* shared between threads or processes */

    unsigned            addr_ntop:1;

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    unsigned            ipv6only:1;

#endif

    unsigned            keepalive:2;

#if (NGX_HAVE_DEFERRED_ACCEPT)

    unsigned            deferred_accept:1;

    unsigned            delete_deferred:1;

    unsigned            add_deferred:1;

#ifdef SO_ACCEPTFILTER

    char               *accept_filter;

#endif

#endif

#if (NGX_HAVE_SETFIB)

    int                 setfib;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

    int                 fastopen;

#endif

};

typedef enum {

     NGX_ERROR_ALERT = 0,

     NGX_ERROR_ERR,

     NGX_ERROR_INFO,

     NGX_ERROR_IGNORE_ECONNRESET,

     NGX_ERROR_IGNORE_EINVAL

‌} ngx_connection_log_error_e;

typedef enum {

     NGX_TCP_NODELAY_UNSET = 0,

     NGX_TCP_NODELAY_SET,

     NGX_TCP_NODELAY_DISABLED

‌} ngx_connection_tcp_nodelay_e;

typedef enum {

     NGX_TCP_NOPUSH_UNSET = 0,

     NGX_TCP_NOPUSH_SET,

     NGX_TCP_NOPUSH_DISABLED

‌} ngx_connection_tcp_nopush_e;

‌#define NGX_LOWLEVEL_BUFFERED  0x0f

‌#define NGX_SSL_BUFFERED       0x01

‌#define NGX_SPDY_BUFFERED      0x02

‌struct ngx_connection_s {

    void               *data;

    ngx_event_t        *read;

    ngx_event_t        *write;

    ngx_socket_t        fd;

    ngx_recv_pt         recv;

    ngx_send_pt         send;

    ngx_recv_chain_pt   recv_chain;

    ngx_send_chain_pt   send_chain;

    ngx_listening_t    *listening;

    off_t               sent;

    ngx_log_t          *log;

    ngx_pool_t         *pool;

    struct sockaddr    *sockaddr;

    socklen_t           socklen;

    ngx_str_t           addr_text;

    ngx_str_t           proxy_protocol_addr;

#if (NGX_SSL)

    ngx_ssl_connection_t  *ssl;

#endif

    struct sockaddr    *local_sockaddr;

    socklen_t           local_socklen;

    ngx_buf_t          *buffer;

    ngx_queue_t         queue;

    ngx_atomic_uint_t   number;

    ngx_uint_t          requests;

    unsigned            buffered:8;

    unsigned            log_error:3;     /* ngx_connection_log_error_e */

    unsigned            unexpected_eof:1;

    unsigned            timedout:1;

    unsigned            error:1;

    unsigned            destroyed:1;

    unsigned            idle:1;

    unsigned            reusable:1;

    unsigned            close:1;

    unsigned            sendfile:1;

    unsigned            sndlowat:1;

    unsigned            tcp_nodelay:2;   /* ngx_connection_tcp_nodelay_e */

    unsigned            tcp_nopush:2;    /* ngx_connection_tcp_nopush_e */

    unsigned            need_last_buf:1;

#if (NGX_HAVE_IOCP)

    unsigned            accept_context_updated:1;

#endif

#if (NGX_HAVE_AIO_SENDFILE)

    unsigned            aio_sendfile:1;

    unsigned            busy_count:2;

    ngx_buf_t          *busy_sendfile;

#endif

#if (NGX_THREADS)

    ngx_atomic_t        lock;

#endif

};

ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr,

    socklen_t socklen);

ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);

ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);

void ngx_configure_listening_sockets(ngx_cycle_t *cycle);

void ngx_close_listening_sockets(ngx_cycle_t *cycle);

void ngx_close_connection(ngx_connection_t *c);

ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,

    ngx_uint_t port);

ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text);

ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log);

void ngx_free_connection(ngx_connection_t *c);

void ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable);

#endif /* _NGX_CONNECTION_H_INCLUDED_ */

src/http/ngx_http_core_module.h - nginx-1.7.9

Data types defined

ngx_http_addr_conf_s

ngx_http_conf_addr_t

ngx_http_conf_port_t

ngx_http_core_loc_conf_s

ngx_http_core_loc_conf_t

ngx_http_core_main_conf_t

ngx_http_core_srv_conf_t

ngx_http_err_page_t

ngx_http_in6_addr_t

ngx_http_in_addr_t

ngx_http_listen_opt_t

ngx_http_location_queue_t

ngx_http_location_tree_node_s

ngx_http_location_tree_node_t

ngx_http_output_body_filter_pt

ngx_http_output_header_filter_pt

ngx_http_phase_engine_t

ngx_http_phase_handler_pt

ngx_http_phase_handler_s

ngx_http_phase_handler_t

ngx_http_phase_t

ngx_http_phases

ngx_http_port_t

ngx_http_server_name_t

ngx_http_try_file_t

ngx_http_virtual_names_t

Macros defined

NGX_HTTP_AIO_OFF

NGX_HTTP_AIO_ON

NGX_HTTP_AIO_SENDFILE

NGX_HTTP_GZIP_PROXIED_ANY

NGX_HTTP_GZIP_PROXIED_AUTH

NGX_HTTP_GZIP_PROXIED_EXPIRED

NGX_HTTP_GZIP_PROXIED_NO_CACHE

NGX_HTTP_GZIP_PROXIED_NO_ETAG

NGX_HTTP_GZIP_PROXIED_NO_LM

NGX_HTTP_GZIP_PROXIED_NO_STORE

NGX_HTTP_GZIP_PROXIED_OFF

NGX_HTTP_GZIP_PROXIED_PRIVATE

NGX_HTTP_IMS_BEFORE

NGX_HTTP_IMS_EXACT

NGX_HTTP_IMS_OFF

NGX_HTTP_KEEPALIVE_DISABLE_MSIE6

NGX_HTTP_KEEPALIVE_DISABLE_NONE

NGX_HTTP_KEEPALIVE_DISABLE_SAFARI

NGX_HTTP_LINGERING_ALWAYS

NGX_HTTP_LINGERING_OFF

NGX_HTTP_LINGERING_ON

NGX_HTTP_SATISFY_ALL

NGX_HTTP_SATISFY_ANY

_NGX_HTTP_CORE_H_INCLUDED_

ngx_http_clear_accept_ranges

ngx_http_clear_content_length

ngx_http_clear_etag

ngx_http_clear_last_modified

ngx_http_clear_location

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_CORE_H_INCLUDED_

‌#define _NGX_HTTP_CORE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_GZIP_PROXIED_OFF       0x0002

‌#define NGX_HTTP_GZIP_PROXIED_EXPIRED   0x0004

‌#define NGX_HTTP_GZIP_PROXIED_NO_CACHE  0x0008

‌#define NGX_HTTP_GZIP_PROXIED_NO_STORE  0x0010

‌#define NGX_HTTP_GZIP_PROXIED_PRIVATE   0x0020

‌#define NGX_HTTP_GZIP_PROXIED_NO_LM     0x0040

‌#define NGX_HTTP_GZIP_PROXIED_NO_ETAG   0x0080

‌#define NGX_HTTP_GZIP_PROXIED_AUTH      0x0100

‌#define NGX_HTTP_GZIP_PROXIED_ANY       0x0200

‌#define NGX_HTTP_AIO_OFF                0

‌#define NGX_HTTP_AIO_ON                 1

‌#define NGX_HTTP_AIO_SENDFILE           2

‌#define NGX_HTTP_SATISFY_ALL            0

‌#define NGX_HTTP_SATISFY_ANY            1

‌#define NGX_HTTP_LINGERING_OFF          0

‌#define NGX_HTTP_LINGERING_ON           1

‌#define NGX_HTTP_LINGERING_ALWAYS       2

‌#define NGX_HTTP_IMS_OFF                0

‌#define NGX_HTTP_IMS_EXACT              1

‌#define NGX_HTTP_IMS_BEFORE             2

‌#define NGX_HTTP_KEEPALIVE_DISABLE_NONE    0x0002

‌#define NGX_HTTP_KEEPALIVE_DISABLE_MSIE6   0x0004

‌#define NGX_HTTP_KEEPALIVE_DISABLE_SAFARI  0x0008

‌typedef struct ngx_http_location_tree_node_s  ngx_http_location_tree_node_t;

‌typedef struct ngx_http_core_loc_conf_s  ngx_http_core_loc_conf_t;

typedef struct {

    union {

        struct sockaddr        sockaddr;

        struct sockaddr_in     sockaddr_in;

#if (NGX_HAVE_INET6)

        struct sockaddr_in6    sockaddr_in6;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        struct sockaddr_un     sockaddr_un;

#endif

        u_char                 sockaddr_data[NGX_SOCKADDRLEN];

    } u;

    socklen_t                  socklen;

    unsigned                   set:1;

    unsigned                   default_server:1;

    unsigned                   bind:1;

    unsigned                   wildcard:1;

#if (NGX_HTTP_SSL)

    unsigned                   ssl:1;

#endif

#if (NGX_HTTP_SPDY)

    unsigned                   spdy:1;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    unsigned                   ipv6only:1;

#endif

    unsigned                   so_keepalive:2;

    unsigned                   proxy_protocol:1;

    int                        backlog;

    int                        rcvbuf;

    int                        sndbuf;

#if (NGX_HAVE_SETFIB)

    int                        setfib;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

    int                        fastopen;

#endif

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    int                        tcp_keepidle;

    int                        tcp_keepintvl;

    int                        tcp_keepcnt;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

    char                      *accept_filter;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

    ngx_uint_t                 deferred_accept;

#endif

    u_char                     addr[NGX_SOCKADDR_STRLEN + 1];

‌} ngx_http_listen_opt_t;

typedef enum {

    NGX_HTTP_POST_READ_PHASE = 0,

    NGX_HTTP_SERVER_REWRITE_PHASE,

    NGX_HTTP_FIND_CONFIG_PHASE,

    NGX_HTTP_REWRITE_PHASE,

    NGX_HTTP_POST_REWRITE_PHASE,

    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,

    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_TRY_FILES_PHASE,

    NGX_HTTP_CONTENT_PHASE,

    NGX_HTTP_LOG_PHASE

‌} ngx_http_phases;

‌typedef struct ngx_http_phase_handler_s  ngx_http_phase_handler_t;

‌typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

‌struct ngx_http_phase_handler_s {

    ngx_http_phase_handler_pt  checker;

    ngx_http_handler_pt        handler;

    ngx_uint_t                 next;

};

typedef struct {

    ngx_http_phase_handler_t  *handlers;

    ngx_uint_t                 server_rewrite_index;

    ngx_uint_t                 location_rewrite_index;

‌} ngx_http_phase_engine_t;

typedef struct {

    ngx_array_t                handlers;

‌} ngx_http_phase_t;

typedef struct {

    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */

    ngx_http_phase_engine_t    phase_engine;

    ngx_hash_t                 headers_in_hash;

    ngx_hash_t                 variables_hash;

    ngx_array_t                variables;       /* ngx_http_variable_t */

    ngx_uint_t                 ncaptures;

    ngx_uint_t                 server_names_hash_max_size;

    ngx_uint_t                 server_names_hash_bucket_size;

    ngx_uint_t                 variables_hash_max_size;

    ngx_uint_t                 variables_hash_bucket_size;

    ngx_hash_keys_arrays_t    *variables_keys;

    ngx_array_t               *ports;

    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */

    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

‌} ngx_http_core_main_conf_t;

typedef struct {

    /* array of the ngx_http_server_name_t, "server_name" directive */

    ngx_array_t                 server_names;

    /* server ctx */

    ngx_http_conf_ctx_t        *ctx;

    ngx_str_t                   server_name;

    size_t                      connection_pool_size;

    size_t                      request_pool_size;

    size_t                      client_header_buffer_size;

    ngx_bufs_t                  large_client_header_buffers;

    ngx_msec_t                  client_header_timeout;

    ngx_flag_t                  ignore_invalid_headers;

    ngx_flag_t                  merge_slashes;

    ngx_flag_t                  underscores_in_headers;

    unsigned                    listen:1;

#if (NGX_PCRE)

    unsigned                    captures:1;

#endif

    ngx_http_core_loc_conf_t  **named_locations;

‌} ngx_http_core_srv_conf_t;

/* list of structures to find core_srv_conf quickly at run time */

typedef struct {

#if (NGX_PCRE)

    ngx_http_regex_t          *regex;

#endif

    ngx_http_core_srv_conf_t  *server;   /* virtual name server conf */

    ngx_str_t                  name;

‌} ngx_http_server_name_t;

typedef struct {

     ngx_hash_combined_t       names;

     ngx_uint_t                nregex;

     ngx_http_server_name_t   *regex;

‌} ngx_http_virtual_names_t;

‌struct ngx_http_addr_conf_s {

    /* the default server configuration for this address:port */

    ngx_http_core_srv_conf_t  *default_server;

    ngx_http_virtual_names_t  *virtual_names;

#if (NGX_HTTP_SSL)

    unsigned                   ssl:1;

#endif

#if (NGX_HTTP_SPDY)

    unsigned                   spdy:1;

#endif

    unsigned                   proxy_protocol:1;

};

typedef struct {

    in_addr_t                  addr;

    ngx_http_addr_conf_t       conf;

‌} ngx_http_in_addr_t;

#if (NGX_HAVE_INET6)

typedef struct {

    struct in6_addr            addr6;

    ngx_http_addr_conf_t       conf;

‌} ngx_http_in6_addr_t;

#endif

typedef struct {

    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */

    void                      *addrs;

    ngx_uint_t                 naddrs;

‌} ngx_http_port_t;

typedef struct {

    ngx_int_t                  family;

    in_port_t                  port;

    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */

‌} ngx_http_conf_port_t;

typedef struct {

    ngx_http_listen_opt_t      opt;

    ngx_hash_t                 hash;

    ngx_hash_wildcard_t       *wc_head;

    ngx_hash_wildcard_t       *wc_tail;

#if (NGX_PCRE)

    ngx_uint_t                 nregex;

    ngx_http_server_name_t    *regex;

#endif

    /* the default server configuration for this address:port */

    ngx_http_core_srv_conf_t  *default_server;

    ngx_array_t                servers;  /* array of ngx_http_core_srv_conf_t */

‌} ngx_http_conf_addr_t;

typedef struct {

    ngx_int_t                  status;

    ngx_int_t                  overwrite;

    ngx_http_complex_value_t   value;

    ngx_str_t                  args;

‌} ngx_http_err_page_t;

typedef struct {

    ngx_array_t               *lengths;

    ngx_array_t               *values;

    ngx_str_t                  name;

    unsigned                   code:10;

    unsigned                   test_dir:1;

‌} ngx_http_try_file_t;

‌struct ngx_http_core_loc_conf_s {

    ngx_str_t     name;          /* location name */

#if (NGX_PCRE)

    ngx_http_regex_t  *regex;

#endif

    unsigned      noname:1;   /* "if () {}" block or limit_except */

    unsigned      lmt_excpt:1;

    unsigned      named:1;

    unsigned      exact_match:1;

    unsigned      noregex:1;

    unsigned      auto_redirect:1;

#if (NGX_HTTP_GZIP)

    unsigned      gzip_disable_msie6:2;

#if (NGX_HTTP_DEGRADATION)

    unsigned      gzip_disable_degradation:2;

#endif

#endif

    ngx_http_location_tree_node_t   *static_locations;

#if (NGX_PCRE)

    ngx_http_core_loc_conf_t       **regex_locations;

#endif

    /* pointer to the modules' loc_conf */

    void        **loc_conf;

    uint32_t      limit_except;

    void        **limit_except_loc_conf;

    ngx_http_handler_pt  handler;

    /* location name length for inclusive location with inherited alias */

    size_t        alias;

    ngx_str_t     root;                    /* root, alias */

    ngx_str_t     post_action;

    ngx_array_t  *root_lengths;

    ngx_array_t  *root_values;

    ngx_array_t  *types;

    ngx_hash_t    types_hash;

    ngx_str_t     default_type;

    off_t         client_max_body_size;    /* client_max_body_size */

    off_t         directio;                /* directio */

    off_t         directio_alignment;      /* directio_alignment */

    size_t        client_body_buffer_size; /* client_body_buffer_size */

    size_t        send_lowat;              /* send_lowat */

    size_t        postpone_output;         /* postpone_output */

    size_t        limit_rate;              /* limit_rate */

    size_t        limit_rate_after;        /* limit_rate_after */

    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */

    size_t        read_ahead;              /* read_ahead */

    ngx_msec_t    client_body_timeout;     /* client_body_timeout */

    ngx_msec_t    send_timeout;            /* send_timeout */

    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */

    ngx_msec_t    lingering_time;          /* lingering_time */

    ngx_msec_t    lingering_timeout;       /* lingering_timeout */

    ngx_msec_t    resolver_timeout;        /* resolver_timeout */

    ngx_resolver_t  *resolver;             /* resolver */

    time_t        keepalive_header;        /* keepalive_timeout */

    ngx_uint_t    keepalive_requests;      /* keepalive_requests */

    ngx_uint_t    keepalive_disable;       /* keepalive_disable */

    ngx_uint_t    satisfy;                 /* satisfy */

    ngx_uint_t    lingering_close;         /* lingering_close */

    ngx_uint_t    if_modified_since;       /* if_modified_since */

    ngx_uint_t    max_ranges;              /* max_ranges */

    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */

    ngx_flag_t    client_body_in_single_buffer;

                                           /* client_body_in_singe_buffer */

    ngx_flag_t    internal;                /* internal */

    ngx_flag_t    sendfile;                /* sendfile */

#if (NGX_HAVE_FILE_AIO)

    ngx_flag_t    aio;                     /* aio */

#endif

    ngx_flag_t    tcp_nopush;              /* tcp_nopush */

    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */

    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */

    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */

    ngx_flag_t    port_in_redirect;        /* port_in_redirect */

    ngx_flag_t    msie_padding;            /* msie_padding */

    ngx_flag_t    msie_refresh;            /* msie_refresh */

    ngx_flag_t    log_not_found;           /* log_not_found */

    ngx_flag_t    log_subrequest;          /* log_subrequest */

    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */

    ngx_flag_t    server_tokens;           /* server_tokens */

    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */

    ngx_flag_t    etag;                    /* etag */

#if (NGX_HTTP_GZIP)

    ngx_flag_t    gzip_vary;               /* gzip_vary */

    ngx_uint_t    gzip_http_version;       /* gzip_http_version */

    ngx_uint_t    gzip_proxied;            /* gzip_proxied */

#if (NGX_PCRE)

    ngx_array_t  *gzip_disable;            /* gzip_disable */

#endif

#endif

#if (NGX_HAVE_OPENAT)

    ngx_uint_t    disable_symlinks;        /* disable_symlinks */

    ngx_http_complex_value_t  *disable_symlinks_from;

#endif

    ngx_array_t  *error_pages;             /* error_page */

    ngx_http_try_file_t    *try_files;     /* try_files */

    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */

    ngx_open_file_cache_t  *open_file_cache;

    time_t        open_file_cache_valid;

    ngx_uint_t    open_file_cache_min_uses;

    ngx_flag_t    open_file_cache_errors;

    ngx_flag_t    open_file_cache_events;

    ngx_log_t    *error_log;

    ngx_uint_t    types_hash_max_size;

    ngx_uint_t    types_hash_bucket_size;

    ngx_queue_t  *locations;

#if 0

    ngx_http_core_loc_conf_t  *prev_location;

#endif

};

typedef struct {

    ngx_queue_t                      queue;

    ngx_http_core_loc_conf_t        *exact;

    ngx_http_core_loc_conf_t        *inclusive;

    ngx_str_t                       *name;

    u_char                          *file_name;

    ngx_uint_t                       line;

    ngx_queue_t                      list;

‌} ngx_http_location_queue_t;

‌struct ngx_http_location_tree_node_s {

    ngx_http_location_tree_node_t   *left;

    ngx_http_location_tree_node_t   *right;

    ngx_http_location_tree_node_t   *tree;

    ngx_http_core_loc_conf_t        *exact;

    ngx_http_core_loc_conf_t        *inclusive;

    u_char                           auto_redirect;

    u_char                           len;

    u_char                           name[1];

};

void ngx_http_core_run_phases(ngx_http_request_t *r);

ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph);

void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);

ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);

void ngx_http_set_exten(ngx_http_request_t *r);

ngx_int_t ngx_http_set_etag(ngx_http_request_t *r);

void ngx_http_weak_etag(ngx_http_request_t *r);

ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,

    ngx_str_t *ct, ngx_http_complex_value_t *cv);

u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,

    size_t *root_length, size_t reserved);

ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);

#if (NGX_HTTP_GZIP)

ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r);

#endif

ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,

    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr,

    ngx_http_post_subrequest_t *psr, ngx_uint_t flags);

ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,

    ngx_str_t *uri, ngx_str_t *args);

ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);

ngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size);

‌typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);

‌typedef ngx_int_t (*ngx_http_output_body_filter_pt)

    (ngx_http_request_t *r, ngx_chain_t *chain);

ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain);

ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);

ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);

ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,

    ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,

    int recursive);

extern ngx_module_t  ngx_http_core_module;

extern ngx_uint_t ngx_http_max_module;

extern ngx_str_t  ngx_http_core_get_method;

‌#define ngx_http_clear_content_length(r)                                      \

                                                                              \

    r->headers_out.content_length_n = -1;                                     \

    if (r->headers_out.content_length) {                                      \

        r->headers_out.content_length->hash = 0;                              \

        r->headers_out.content_length = NULL;                                 \

    }

‌#define ngx_http_clear_accept_ranges(r)                                       \

                                                                              \

    r->allow_ranges = 0;                                                      \

    if (r->headers_out.accept_ranges) {                                       \

        r->headers_out.accept_ranges->hash = 0;                               \

        r->headers_out.accept_ranges = NULL;                                  \

    }

‌#define ngx_http_clear_last_modified(r)                                       \

                                                                              \

    r->headers_out.last_modified_time = -1;                                   \

    if (r->headers_out.last_modified) {                                       \

        r->headers_out.last_modified->hash = 0;                               \

        r->headers_out.last_modified = NULL;                                  \

    }

‌#define ngx_http_clear_location(r)                                            \

                                                                              \

    if (r->headers_out.location) {                                            \

        r->headers_out.location->hash = 0;                                    \

        r->headers_out.location = NULL;                                       \

    }

‌#define ngx_http_clear_etag(r)                                                \

                                                                              \

    if (r->headers_out.etag) {                                                \

        r->headers_out.etag->hash = 0;                                        \

        r->headers_out.etag = NULL;                                           \

    }

#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */

src/core/ngx_inet.h - nginx-1.7.9

Data types defined

ngx_addr_t

ngx_cidr_t

ngx_in6_cidr_t

ngx_in_cidr_t

ngx_url_t

Macros defined

NGX_INET6_ADDRSTRLEN

NGX_INET_ADDRSTRLEN

NGX_SOCKADDRLEN

NGX_SOCKADDRLEN

NGX_SOCKADDR_STRLEN

NGX_SOCKADDR_STRLEN

NGX_UNIX_ADDRSTRLEN

_NGX_INET_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_INET_H_INCLUDED_

‌#define _NGX_INET_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

/*

* TODO: autoconfigure NGX_SOCKADDRLEN and NGX_SOCKADDR_STRLEN as

*       sizeof(struct sockaddr_storage)

*       sizeof(struct sockaddr_un)

*       sizeof(struct sockaddr_in6)

*       sizeof(struct sockaddr_in)

*/

‌#define NGX_INET_ADDRSTRLEN   (sizeof("255.255.255.255") - 1)

‌#define NGX_INET6_ADDRSTRLEN                                                 \

    (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1)

‌#define NGX_UNIX_ADDRSTRLEN                                                  \

    (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))

#if (NGX_HAVE_UNIX_DOMAIN)

‌#define NGX_SOCKADDR_STRLEN   (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN)

#else

‌#define NGX_SOCKADDR_STRLEN   (NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1)

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

‌#define NGX_SOCKADDRLEN       sizeof(struct sockaddr_un)

#else

‌#define NGX_SOCKADDRLEN       512

#endif

typedef struct {

    in_addr_t                 addr;

    in_addr_t                 mask;

‌} ngx_in_cidr_t;

#if (NGX_HAVE_INET6)

typedef struct {

    struct in6_addr           addr;

    struct in6_addr           mask;

‌} ngx_in6_cidr_t;

#endif

typedef struct {

    ngx_uint_t                family;

    union {

        ngx_in_cidr_t         in;

#if (NGX_HAVE_INET6)

        ngx_in6_cidr_t        in6;

#endif

    } u;

‌} ngx_cidr_t;

typedef struct {

    struct sockaddr          *sockaddr;

    socklen_t                 socklen;

    ngx_str_t                 name;

‌} ngx_addr_t;

typedef struct {

    ngx_str_t                 url;

    ngx_str_t                 host;

    ngx_str_t                 port_text;

    ngx_str_t                 uri;

    in_port_t                 port;

    in_port_t                 default_port;

    int                       family;

    unsigned                  listen:1;

    unsigned                  uri_part:1;

    unsigned                  no_resolve:1;

    unsigned                  one_addr:1;  /* compatibility */

    unsigned                  no_port:1;

    unsigned                  wildcard:1;

    socklen_t                 socklen;

    u_char                    sockaddr[NGX_SOCKADDRLEN];

    ngx_addr_t               *addrs;

    ngx_uint_t                naddrs;

    char                     *err;

‌} ngx_url_t;

in_addr_t ngx_inet_addr(u_char *text, size_t len);

#if (NGX_HAVE_INET6)

ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr);

size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len);

#endif

size_t ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text,

    size_t len, ngx_uint_t port);

size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len);

ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr);

ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,

    size_t len);

ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u);

ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u);

ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1,

    struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port);

#endif /* _NGX_INET_H_INCLUDED_ */

src/core/ngx_inet.c - nginx-1.7.9

Functions defined

ngx_cmp_sockaddr

ngx_inet6_addr

ngx_inet6_ntop

ngx_inet_addr

ngx_inet_ntop

ngx_inet_resolve_host

ngx_inet_resolve_host

ngx_parse_addr

ngx_parse_inet6_url

ngx_parse_inet_url

ngx_parse_unix_domain_url

ngx_parse_url

ngx_ptocidr

ngx_sock_ntop

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u);

static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u);

static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u);

in_addr_t

‌ngx_inet_addr(u_char *text, size_t len)

{

    u_char      *p, c;

    in_addr_t    addr;

    ngx_uint_t   octet, n;

    addr = 0;

    octet = 0;

    n = 0;

    for (p = text; p < text + len; p++) {

        c = *p;

        if (c >= '0' && c <= '9') {

            octet = octet * 10 + (c - '0');

            continue;

        }

        if (c == '.' && octet < 256) {

            addr = (addr << 8) + octet;

            octet = 0;

            n++;

            continue;

        }

        return INADDR_NONE;

    }

    if (n == 3 && octet < 256) {

        addr = (addr << 8) + octet;

        return htonl(addr);

    }

    return INADDR_NONE;

}

#if (NGX_HAVE_INET6)

ngx_int_t

‌ngx_inet6_addr(u_char *p, size_t len, u_char *addr)

{

    u_char      c, *zero, *digit, *s, *d;

    size_t      len4;

    ngx_uint_t  n, nibbles, word;

    if (len == 0) {

        return NGX_ERROR;

    }

    zero = NULL;

    digit = NULL;

    len4 = 0;

    nibbles = 0;

    word = 0;

    n = 8;

    if (p[0] == ':') {

        p++;

        len--;

    }

    for (/* void */; len; len--) {

        c = *p++;

        if (c == ':') {

            if (nibbles) {

                digit = p;

                len4 = len;

                *addr++ = (u_char) (word >> 8);

                *addr++ = (u_char) (word & 0xff);

                if (--n) {

                    nibbles = 0;

                    word = 0;

                    continue;

                }

            } else {

                if (zero == NULL) {

                    digit = p;

                    len4 = len;

                    zero = addr;

                    continue;

                }

            }

            return NGX_ERROR;

        }

        if (c == '.' && nibbles) {

            if (n < 2 || digit == NULL) {

                return NGX_ERROR;

            }

            word = ngx_inet_addr(digit, len4 - 1);

            if (word == INADDR_NONE) {

                return NGX_ERROR;

            }

            word = ntohl(word);

            *addr++ = (u_char) ((word >> 24) & 0xff);

            *addr++ = (u_char) ((word >> 16) & 0xff);

            n--;

            break;

        }

        if (++nibbles > 4) {

            return NGX_ERROR;

        }

        if (c >= '0' && c <= '9') {

            word = word * 16 + (c - '0');

            continue;

        }

        c |= 0x20;

        if (c >= 'a' && c <= 'f') {

            word = word * 16 + (c - 'a') + 10;

            continue;

        }

        return NGX_ERROR;

    }

    if (nibbles == 0 && zero == NULL) {

        return NGX_ERROR;

    }

    *addr++ = (u_char) (word >> 8);

    *addr++ = (u_char) (word & 0xff);

    if (--n) {

        if (zero) {

            n *= 2;

            s = addr - 1;

            d = s + n;

            while (s >= zero) {

                *d-- = *s--;

            }

            ngx_memzero(zero, n);

            return NGX_OK;

        }

    } else {

        if (zero == NULL) {

            return NGX_OK;

        }

    }

    return NGX_ERROR;

}

#endif

size_t

‌ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len,

    ngx_uint_t port)

{

    u_char               *p;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    size_t                n;

    struct sockaddr_in6  *sin6;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    struct sockaddr_un   *saun;

#endif

    switch (sa->sa_family) {

    case AF_INET:

        sin = (struct sockaddr_in *) sa;

        p = (u_char *) &sin->sin_addr;

        if (port) {

            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d",

                             p[0], p[1], p[2], p[3], ntohs(sin->sin_port));

        } else {

            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",

                             p[0], p[1], p[2], p[3]);

        }

        return (p - text);

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) sa;

        n = 0;

        if (port) {

            text[n++] = '[';

        }

        n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len);

        if (port) {

            n = ngx_sprintf(&text[1 + n], "]:%d",

                            ntohs(sin6->sin6_port)) - text;

        }

        return n;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        saun = (struct sockaddr_un *) sa;

        /* on Linux sockaddr might not include sun_path at all */

        if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {

            p = ngx_snprintf(text, len, "unix:%Z");

        } else {

            p = ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path);

        }

        /* we do not include trailing zero in address length */

        return (p - text - 1);

#endif

    default:

        return 0;

    }

}

size_t

‌ngx_inet_ntop(int family, void *addr, u_char *text, size_t len)

{

    u_char  *p;

    switch (family) {

    case AF_INET:

        p = addr;

        return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",

                            p[0], p[1], p[2], p[3])

               - text;

#if (NGX_HAVE_INET6)

    case AF_INET6:

        return ngx_inet6_ntop(addr, text, len);

#endif

    default:

        return 0;

    }

}

#if (NGX_HAVE_INET6)

size_t

‌ngx_inet6_ntop(u_char *p, u_char *text, size_t len)

{

    u_char      *dst;

    size_t       max, n;

    ngx_uint_t   i, zero, last;

    if (len < NGX_INET6_ADDRSTRLEN) {

        return 0;

    }

    zero = (ngx_uint_t) -1;

    last = (ngx_uint_t) -1;

    max = 1;

    n = 0;

    for (i = 0; i < 16; i += 2) {

        if (p[i] || p[i + 1]) {

            if (max < n) {

                zero = last;

                max = n;

            }

            n = 0;

            continue;

        }

        if (n++ == 0) {

            last = i;

        }

    }

    if (max < n) {

        zero = last;

        max = n;

    }

    dst = text;

    n = 16;

    if (zero == 0) {

        if ((max == 5 && p[10] == 0xff && p[11] == 0xff)

            || (max == 6)

            || (max == 7 && p[14] != 0 && p[15] != 1))

        {

            n = 12;

        }

        *dst++ = ':';

    }

    for (i = 0; i < n; i += 2) {

        if (i == zero) {

            *dst++ = ':';

            i += (max - 1) * 2;

            continue;

        }

        dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]);

        if (i < 14) {

            *dst++ = ':';

        }

    }

    if (n == 12) {

        dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]);

    }

    return dst - text;

}

#endif

ngx_int_t

‌ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr)

{

    u_char      *addr, *mask, *last;

    size_t       len;

    ngx_int_t    shift;

#if (NGX_HAVE_INET6)

    ngx_int_t    rc;

    ngx_uint_t   s, i;

#endif

    addr = text->data;

    last = addr + text->len;

    mask = ngx_strlchr(addr, last, '/');

    len = (mask ? mask : last) - addr;

    cidr->u.in.addr = ngx_inet_addr(addr, len);

    if (cidr->u.in.addr != INADDR_NONE) {

        cidr->family = AF_INET;

        if (mask == NULL) {

            cidr->u.in.mask = 0xffffffff;

            return NGX_OK;

        }

#if (NGX_HAVE_INET6)

    } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) {

        cidr->family = AF_INET6;

        if (mask == NULL) {

            ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16);

            return NGX_OK;

        }

#endif

    } else {

        return NGX_ERROR;

    }

    mask++;

    shift = ngx_atoi(mask, last - mask);

    if (shift == NGX_ERROR) {

        return NGX_ERROR;

    }

    switch (cidr->family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        if (shift > 128) {

            return NGX_ERROR;

        }

        addr = cidr->u.in6.addr.s6_addr;

        mask = cidr->u.in6.mask.s6_addr;

        rc = NGX_OK;

        for (i = 0; i < 16; i++) {

            s = (shift > 8) ? 8 : shift;

            shift -= s;

            mask[i] = (u_char) (0xffu << (8 - s));

            if (addr[i] != (addr[i] & mask[i])) {

                rc = NGX_DONE;

                addr[i] &= mask[i];

            }

        }

        return rc;

#endif

    default: /* AF_INET */

        if (shift > 32) {

            return NGX_ERROR;

        }

        if (shift) {

            cidr->u.in.mask = htonl((uint32_t) (0xffffffffu << (32 - shift)));

        } else {

            /* x86 compilers use a shl instruction that shifts by modulo 32 */

            cidr->u.in.mask = 0;

        }

        if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) {

            return NGX_OK;

        }

        cidr->u.in.addr &= cidr->u.in.mask;

        return NGX_DONE;

    }

}

ngx_int_t

‌ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len)

{

    in_addr_t             inaddr;

    ngx_uint_t            family;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct in6_addr       inaddr6;

    struct sockaddr_in6  *sin6;

    /*

     * prevent MSVC8 warning:

     *    potentially uninitialized local variable 'inaddr6' used

     */

    ngx_memzero(&inaddr6, sizeof(struct in6_addr));

#endif

    inaddr = ngx_inet_addr(text, len);

    if (inaddr != INADDR_NONE) {

        family = AF_INET;

        len = sizeof(struct sockaddr_in);

#if (NGX_HAVE_INET6)

    } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) {

        family = AF_INET6;

        len = sizeof(struct sockaddr_in6);

#endif

    } else {

        return NGX_DECLINED;

    }

    addr->sockaddr = ngx_pcalloc(pool, len);

    if (addr->sockaddr == NULL) {

        return NGX_ERROR;

    }

    addr->sockaddr->sa_family = (u_char) family;

    addr->socklen = len;

    switch (family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) addr->sockaddr;

        ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) addr->sockaddr;

        sin->sin_addr.s_addr = inaddr;

        break;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)

{

    u_char  *p;

    p = u->url.data;

    if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {

        return ngx_parse_unix_domain_url(pool, u);

    }

    if (p[0] == '[') {

        return ngx_parse_inet6_url(pool, u);

    }

    return ngx_parse_inet_url(pool, u);

}

static ngx_int_t

‌ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u)

{

#if (NGX_HAVE_UNIX_DOMAIN)

    u_char              *path, *uri, *last;

    size_t               len;

    struct sockaddr_un  *saun;

    len = u->url.len;

    path = u->url.data;

    path += 5;

    len -= 5;

    if (u->uri_part) {

        last = path + len;

        uri = ngx_strlchr(path, last, ':');

        if (uri) {

            len = uri - path;

            uri++;

            u->uri.len = last - uri;

            u->uri.data = uri;

        }

    }

    if (len == 0) {

        u->err = "no path in the unix domain socket";

        return NGX_ERROR;

    }

    u->host.len = len++;

    u->host.data = path;

    if (len > sizeof(saun->sun_path)) {

        u->err = "too long path in the unix domain socket";

        return NGX_ERROR;

    }

    u->socklen = sizeof(struct sockaddr_un);

    saun = (struct sockaddr_un *) &u->sockaddr;

    saun->sun_family = AF_UNIX;

    (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);

    u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));

    if (u->addrs == NULL) {

        return NGX_ERROR;

    }

    saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un));

    if (saun == NULL) {

        return NGX_ERROR;

    }

    u->family = AF_UNIX;

    u->naddrs = 1;

    saun->sun_family = AF_UNIX;

    (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);

    u->addrs[0].sockaddr = (struct sockaddr *) saun;

    u->addrs[0].socklen = sizeof(struct sockaddr_un);

    u->addrs[0].name.len = len + 4;

    u->addrs[0].name.data = u->url.data;

    return NGX_OK;

#else

    u->err = "the unix domain sockets are not supported on this platform";

    return NGX_ERROR;

#endif

}

static ngx_int_t

‌ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)

{

    u_char               *p, *host, *port, *last, *uri, *args;

    size_t                len;

    ngx_int_t             n;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    u->socklen = sizeof(struct sockaddr_in);

    sin = (struct sockaddr_in *) &u->sockaddr;

    sin->sin_family = AF_INET;

    u->family = AF_INET;

    host = u->url.data;

    last = host + u->url.len;

    port = ngx_strlchr(host, last, ':');

    uri = ngx_strlchr(host, last, '/');

    args = ngx_strlchr(host, last, '?');

    if (args) {

        if (uri == NULL || args < uri) {

            uri = args;

        }

    }

    if (uri) {

        if (u->listen || !u->uri_part) {

            u->err = "invalid host";

            return NGX_ERROR;

        }

        u->uri.len = last - uri;

        u->uri.data = uri;

        last = uri;

        if (uri < port) {

            port = NULL;

        }

    }

    if (port) {

        port++;

        len = last - port;

        n = ngx_atoi(port, len);

        if (n < 1 || n > 65535) {

            u->err = "invalid port";

            return NGX_ERROR;

        }

        u->port = (in_port_t) n;

        sin->sin_port = htons((in_port_t) n);

        u->port_text.len = len;

        u->port_text.data = port;

        last = port - 1;

    } else {

        if (uri == NULL) {

            if (u->listen) {

                /* test value as port only */

                n = ngx_atoi(host, last - host);

                if (n != NGX_ERROR) {

                    if (n < 1 || n > 65535) {

                        u->err = "invalid port";

                        return NGX_ERROR;

                    }

                    u->port = (in_port_t) n;

                    sin->sin_port = htons((in_port_t) n);

                    u->port_text.len = last - host;

                    u->port_text.data = host;

                    u->wildcard = 1;

                    return NGX_OK;

                }

            }

        }

        u->no_port = 1;

        u->port = u->default_port;

        sin->sin_port = htons(u->default_port);

    }

    len = last - host;

    if (len == 0) {

        u->err = "no host";

        return NGX_ERROR;

    }

    u->host.len = len;

    u->host.data = host;

    if (u->listen && len == 1 && *host == '*') {

        sin->sin_addr.s_addr = INADDR_ANY;

        u->wildcard = 1;

        return NGX_OK;

    }

    sin->sin_addr.s_addr = ngx_inet_addr(host, len);

    if (sin->sin_addr.s_addr != INADDR_NONE) {

        if (sin->sin_addr.s_addr == INADDR_ANY) {

            u->wildcard = 1;

        }

        u->naddrs = 1;

        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));

        if (u->addrs == NULL) {

            return NGX_ERROR;

        }

        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));

        if (sin == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(sin, u->sockaddr, sizeof(struct sockaddr_in));

        u->addrs[0].sockaddr = (struct sockaddr *) sin;

        u->addrs[0].socklen = sizeof(struct sockaddr_in);

        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);

        if (p == NULL) {

            return NGX_ERROR;

        }

        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",

                                           &u->host, u->port) - p;

        u->addrs[0].name.data = p;

        return NGX_OK;

    }

    if (u->no_resolve) {

        return NGX_OK;

    }

    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {

        return NGX_ERROR;

    }

    u->family = u->addrs[0].sockaddr->sa_family;

    u->socklen = u->addrs[0].socklen;

    ngx_memcpy(u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);

    switch (u->family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) &u->sockaddr;

        if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {

            u->wildcard = 1;

        }

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) &u->sockaddr;

        if (sin->sin_addr.s_addr == INADDR_ANY) {

            u->wildcard = 1;

        }

        break;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)

{

#if (NGX_HAVE_INET6)

    u_char               *p, *host, *port, *last, *uri;

    size_t                len;

    ngx_int_t             n;

    struct sockaddr_in6  *sin6;

    u->socklen = sizeof(struct sockaddr_in6);

    sin6 = (struct sockaddr_in6 *) &u->sockaddr;

    sin6->sin6_family = AF_INET6;

    host = u->url.data + 1;

    last = u->url.data + u->url.len;

    p = ngx_strlchr(host, last, ']');

    if (p == NULL) {

        u->err = "invalid host";

        return NGX_ERROR;

    }

    if (last - p) {

        port = p + 1;

        uri = ngx_strlchr(port, last, '/');

        if (uri) {

            if (u->listen || !u->uri_part) {

                u->err = "invalid host";

                return NGX_ERROR;

            }

            u->uri.len = last - uri;

            u->uri.data = uri;

            last = uri;

        }

        if (*port == ':') {

            port++;

            len = last - port;

            n = ngx_atoi(port, len);

            if (n < 1 || n > 65535) {

                u->err = "invalid port";

                return NGX_ERROR;

            }

            u->port = (in_port_t) n;

            sin6->sin6_port = htons((in_port_t) n);

            u->port_text.len = len;

            u->port_text.data = port;

        } else {

            u->no_port = 1;

            u->port = u->default_port;

            sin6->sin6_port = htons(u->default_port);

        }

    }

    len = p - host;

    if (len == 0) {

        u->err = "no host";

        return NGX_ERROR;

    }

    u->host.len = len + 2;

    u->host.data = host - 1;

    if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) {

        u->err = "invalid IPv6 address";

        return NGX_ERROR;

    }

    if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {

        u->wildcard = 1;

    }

    u->family = AF_INET6;

    u->naddrs = 1;

    u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));

    if (u->addrs == NULL) {

        return NGX_ERROR;

    }

    sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));

    if (sin6 == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(sin6, u->sockaddr, sizeof(struct sockaddr_in6));

    u->addrs[0].sockaddr = (struct sockaddr *) sin6;

    u->addrs[0].socklen = sizeof(struct sockaddr_in6);

    p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);

    if (p == NULL) {

        return NGX_ERROR;

    }

    u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",

                                       &u->host, u->port) - p;

    u->addrs[0].name.data = p;

    return NGX_OK;

#else

    u->err = "the INET6 sockets are not supported on this platform";

    return NGX_ERROR;

#endif

}

#if (NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6)

ngx_int_t

‌ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)

{

    u_char               *p, *host;

    size_t                len;

    in_port_t             port;

    ngx_uint_t            i;

    struct addrinfo       hints, *res, *rp;

    struct sockaddr_in   *sin;

    struct sockaddr_in6  *sin6;

    port = htons(u->port);

    host = ngx_alloc(u->host.len + 1, pool->log);

    if (host == NULL) {

        return NGX_ERROR;

    }

    (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);

    ngx_memzero(&hints, sizeof(struct addrinfo));

    hints.ai_family = AF_UNSPEC;

    hints.ai_socktype = SOCK_STREAM;

#ifdef AI_ADDRCONFIG

    hints.ai_flags = AI_ADDRCONFIG;

#endif

    if (getaddrinfo((char *) host, NULL, &hints, &res) != 0) {

        u->err = "host not found";

        ngx_free(host);

        return NGX_ERROR;

    }

    ngx_free(host);

    for (i = 0, rp = res; rp != NULL; rp = rp->ai_next) {

        switch (rp->ai_family) {

        case AF_INET:

        case AF_INET6:

            break;

        default:

            continue;

        }

        i++;

    }

    if (i == 0) {

        u->err = "host not found";

        goto failed;

    }

    /* MP: ngx_shared_palloc() */

    u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));

    if (u->addrs == NULL) {

        goto failed;

    }

    u->naddrs = i;

    i = 0;

    /* AF_INET addresses first */

    for (rp = res; rp != NULL; rp = rp->ai_next) {

        if (rp->ai_family != AF_INET) {

            continue;

        }

        sin = ngx_pcalloc(pool, rp->ai_addrlen);

        if (sin == NULL) {

            goto failed;

        }

        ngx_memcpy(sin, rp->ai_addr, rp->ai_addrlen);

        sin->sin_port = port;

        u->addrs[i].sockaddr = (struct sockaddr *) sin;

        u->addrs[i].socklen = rp->ai_addrlen;

        len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;

        p = ngx_pnalloc(pool, len);

        if (p == NULL) {

            goto failed;

        }

        len = ngx_sock_ntop((struct sockaddr *) sin, rp->ai_addrlen, p, len, 1);

        u->addrs[i].name.len = len;

        u->addrs[i].name.data = p;

        i++;

    }

    for (rp = res; rp != NULL; rp = rp->ai_next) {

        if (rp->ai_family != AF_INET6) {

            continue;

        }

        sin6 = ngx_pcalloc(pool, rp->ai_addrlen);

        if (sin6 == NULL) {

            goto failed;

        }

        ngx_memcpy(sin6, rp->ai_addr, rp->ai_addrlen);

        sin6->sin6_port = port;

        u->addrs[i].sockaddr = (struct sockaddr *) sin6;

        u->addrs[i].socklen = rp->ai_addrlen;

        len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;

        p = ngx_pnalloc(pool, len);

        if (p == NULL) {

            goto failed;

        }

        len = ngx_sock_ntop((struct sockaddr *) sin6, rp->ai_addrlen, p,

                            len, 1);

        u->addrs[i].name.len = len;

        u->addrs[i].name.data = p;

        i++;

    }

    freeaddrinfo(res);

    return NGX_OK;

failed:

    freeaddrinfo(res);

    return NGX_ERROR;

}

#else /* !NGX_HAVE_GETADDRINFO || !NGX_HAVE_INET6 */

ngx_int_t

‌ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)

{

    u_char              *p, *host;

    size_t               len;

    in_port_t            port;

    in_addr_t            in_addr;

    ngx_uint_t           i;

    struct hostent      *h;

    struct sockaddr_in  *sin;

    /* AF_INET only */

    port = htons(u->port);

    in_addr = ngx_inet_addr(u->host.data, u->host.len);

    if (in_addr == INADDR_NONE) {

        host = ngx_alloc(u->host.len + 1, pool->log);

        if (host == NULL) {

            return NGX_ERROR;

        }

        (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);

        h = gethostbyname((char *) host);

        ngx_free(host);

        if (h == NULL || h->h_addr_list[0] == NULL) {

            u->err = "host not found";

            return NGX_ERROR;

        }

        for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }

        /* MP: ngx_shared_palloc() */

        u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));

        if (u->addrs == NULL) {

            return NGX_ERROR;

        }

        u->naddrs = i;

        for (i = 0; i < u->naddrs; i++) {

            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));

            if (sin == NULL) {

                return NGX_ERROR;

            }

            sin->sin_family = AF_INET;

            sin->sin_port = port;

            sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);

            u->addrs[i].sockaddr = (struct sockaddr *) sin;

            u->addrs[i].socklen = sizeof(struct sockaddr_in);

            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;

            p = ngx_pnalloc(pool, len);

            if (p == NULL) {

                return NGX_ERROR;

            }

            len = ngx_sock_ntop((struct sockaddr *) sin,

                                sizeof(struct sockaddr_in), p, len, 1);

            u->addrs[i].name.len = len;

            u->addrs[i].name.data = p;

        }

    } else {

        /* MP: ngx_shared_palloc() */

        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));

        if (u->addrs == NULL) {

            return NGX_ERROR;

        }

        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));

        if (sin == NULL) {

            return NGX_ERROR;

        }

        u->naddrs = 1;

        sin->sin_family = AF_INET;

        sin->sin_port = port;

        sin->sin_addr.s_addr = in_addr;

        u->addrs[0].sockaddr = (struct sockaddr *) sin;

        u->addrs[0].socklen = sizeof(struct sockaddr_in);

        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);

        if (p == NULL) {

            return NGX_ERROR;

        }

        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",

                                           &u->host, ntohs(port)) - p;

        u->addrs[0].name.data = p;

    }

    return NGX_OK;

}

#endif /* NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6 */

ngx_int_t

‌ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1,

    struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port)

{

    struct sockaddr_in   *sin1, *sin2;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin61, *sin62;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    struct sockaddr_un   *saun1, *saun2;

#endif

    if (sa1->sa_family != sa2->sa_family) {

        return NGX_DECLINED;

    }

    switch (sa1->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin61 = (struct sockaddr_in6 *) sa1;

        sin62 = (struct sockaddr_in6 *) sa2;

        if (cmp_port && sin61->sin6_port != sin62->sin6_port) {

            return NGX_DECLINED;

        }

        if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) {

            return NGX_DECLINED;

        }

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

       /* TODO length */

       saun1 = (struct sockaddr_un *) sa1;

       saun2 = (struct sockaddr_un *) sa2;

       if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path,

                      sizeof(saun1->sun_path))

           != 0)

       {

           return NGX_DECLINED;

       }

       break;

#endif

    default: /* AF_INET */

        sin1 = (struct sockaddr_in *) sa1;

        sin2 = (struct sockaddr_in *) sa2;

        if (cmp_port && sin1->sin_port != sin2->sin_port) {

            return NGX_DECLINED;

        }

        if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {

            return NGX_DECLINED;

        }

        break;

    }

    return NGX_OK;

}

src/http/ngx_http.h - nginx-1.7.9

Data types defined

ngx_http_cache_t

ngx_http_chunked_s

ngx_http_chunked_t

ngx_http_file_cache_t

ngx_http_header_handler_pt

ngx_http_log_ctx_s

ngx_http_log_ctx_t

ngx_http_log_handler_pt

ngx_http_request_t

ngx_http_spdy_stream_t

ngx_http_status_t

ngx_http_upstream_t

Macros defined

NGX_HTTP_FLUSH

NGX_HTTP_LAST

_NGX_HTTP_H_INCLUDED_

ngx_http_ephemeral

ngx_http_get_module_ctx

ngx_http_set_ctx

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_H_INCLUDED_

‌#define _NGX_HTTP_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef struct ngx_http_request_s     ngx_http_request_t;

‌typedef struct ngx_http_upstream_s    ngx_http_upstream_t;

‌typedef struct ngx_http_cache_s       ngx_http_cache_t;

‌typedef struct ngx_http_file_cache_s  ngx_http_file_cache_t;

‌typedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;

‌typedef struct ngx_http_chunked_s     ngx_http_chunked_t;

#if (NGX_HTTP_SPDY)

‌typedef struct ngx_http_spdy_stream_s  ngx_http_spdy_stream_t;

#endif

‌typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

‌typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,

    ngx_http_request_t *sr, u_char *buf, size_t len);

#include <ngx_http_variables.h>

#include <ngx_http_config.h>

#include <ngx_http_request.h>

#include <ngx_http_script.h>

#include <ngx_http_upstream.h>

#include <ngx_http_upstream_round_robin.h>

#include <ngx_http_busy_lock.h>

#include <ngx_http_core_module.h>

#if (NGX_HTTP_SPDY)

#include <ngx_http_spdy.h>

#endif

#if (NGX_HTTP_CACHE)

#include <ngx_http_cache.h>

#endif

#if (NGX_HTTP_SSI)

#include <ngx_http_ssi_filter_module.h>

#endif

#if (NGX_HTTP_SSL)

#include <ngx_http_ssl_module.h>

#endif

‌struct ngx_http_log_ctx_s {

    ngx_connection_t    *connection;

    ngx_http_request_t  *request;

    ngx_http_request_t  *current_request;

};

‌struct ngx_http_chunked_s {

    ngx_uint_t           state;

    off_t                size;

    off_t                length;

};

typedef struct {

    ngx_uint_t           http_version;

    ngx_uint_t           code;

    ngx_uint_t           count;

    u_char              *start;

    u_char              *end;

‌} ngx_http_status_t;

‌#define ngx_http_get_module_ctx(r, module)  (r)->ctx[module.ctx_index]

‌#define ngx_http_set_ctx(r, c, module)      r->ctx[module.ctx_index] = c;

ngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,

    ngx_http_core_loc_conf_t *clcf);

ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_listen_opt_t *lsopt);

void ngx_http_init_connection(ngx_connection_t *c);

void ngx_http_close_connection(ngx_connection_t *c);

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);

#endif

ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);

ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);

ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,

    ngx_uint_t merge_slashes);

ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_http_status_t *status);

ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,

    ngx_str_t *args, ngx_uint_t *flags);

ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_uint_t allow_underscores);

ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,

    ngx_str_t *name, ngx_str_t *value);

ngx_int_t ngx_http_parse_set_cookie_lines(ngx_array_t *headers,

    ngx_str_t *name, ngx_str_t *value);

ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,

    ngx_str_t *value);

void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,

    ngx_str_t *args);

ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_http_chunked_t *ctx);

ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);

ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);

ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);

void ngx_http_process_request(ngx_http_request_t *r);

void ngx_http_update_location_config(ngx_http_request_t *r);

void ngx_http_handler(ngx_http_request_t *r);

void ngx_http_run_posted_requests(ngx_connection_t *c);

ngx_int_t ngx_http_post_request(ngx_http_request_t *r,

    ngx_http_posted_request_t *pr);

void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);

void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);

void ngx_http_empty_handler(ngx_event_t *wev);

void ngx_http_request_empty_handler(ngx_http_request_t *r);

‌#define ngx_http_ephemeral(r)  (void *) (&r->uri_start)

‌#define NGX_HTTP_LAST   1

‌#define NGX_HTTP_FLUSH  2

ngx_int_t ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags);

ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,

    ngx_http_client_body_handler_pt post_handler);

ngx_int_t ngx_http_send_header(ngx_http_request_t *r);

ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,

    ngx_int_t error);

ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,

    ngx_module_t *m, ngx_int_t error);

void ngx_http_clean_header(ngx_http_request_t *r);

time_t ngx_http_parse_time(u_char *value, size_t len);

size_t ngx_http_get_time(char *buf, time_t t);

ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);

void ngx_http_discarded_request_body_handler(ngx_http_request_t *r);

void ngx_http_block_reading(ngx_http_request_t *r);

void ngx_http_test_reading(ngx_http_request_t *r);

char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys,

    ngx_hash_t *types_hash, ngx_array_t **prev_keys,

    ngx_hash_t *prev_types_hash, ngx_str_t *default_types);

ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,

    ngx_str_t *default_type);

#if (NGX_HTTP_DEGRADATION)

ngx_uint_t  ngx_http_degraded(ngx_http_request_t *);

#endif

extern ngx_module_t  ngx_http_module;

extern ngx_str_t  ngx_http_html_default_types[];

extern ngx_http_output_header_filter_pt  ngx_http_top_header_filter;

extern ngx_http_output_body_filter_pt    ngx_http_top_body_filter;

#endif /* _NGX_HTTP_H_INCLUDED_ */

src/http/ngx_http_request.h - nginx-1.7.9

Data types defined

ngx_http_addr_conf_t

ngx_http_cleanup_pt

ngx_http_cleanup_s

ngx_http_cleanup_t

ngx_http_client_body_handler_pt

ngx_http_connection_t

ngx_http_ephemeral_t

ngx_http_event_handler_pt

ngx_http_handler_pt

ngx_http_header_out_t

ngx_http_header_t

ngx_http_headers_in_t

ngx_http_headers_out_t

ngx_http_post_subrequest_pt

ngx_http_post_subrequest_t

ngx_http_posted_request_s

ngx_http_posted_request_t

ngx_http_postponed_request_s

ngx_http_postponed_request_t

ngx_http_request_body_t

ngx_http_request_s

ngx_http_state_e

Macros defined

NGX_HTTPS_CERT_ERROR

NGX_HTTPS_NO_CERT

NGX_HTTP_ACCEPTED

NGX_HTTP_BAD_GATEWAY

NGX_HTTP_BAD_REQUEST

NGX_HTTP_CLIENT_CLOSED_REQUEST

NGX_HTTP_CLIENT_ERROR

NGX_HTTP_CLOSE

NGX_HTTP_CONFLICT

NGX_HTTP_CONNECTION_CLOSE

NGX_HTTP_CONNECTION_KEEP_ALIVE

NGX_HTTP_CONTINUE

NGX_HTTP_COPY

NGX_HTTP_COPY_BUFFERED

NGX_HTTP_CREATED

NGX_HTTP_DELETE

NGX_HTTP_DISCARD_BUFFER_SIZE

NGX_HTTP_FORBIDDEN

NGX_HTTP_GATEWAY_TIME_OUT

NGX_HTTP_GET

NGX_HTTP_GZIP_BUFFERED

NGX_HTTP_HEAD

NGX_HTTP_INSUFFICIENT_STORAGE

NGX_HTTP_INTERNAL_SERVER_ERROR

NGX_HTTP_LC_HEADER_LEN

NGX_HTTP_LENGTH_REQUIRED

NGX_HTTP_LINGERING_BUFFER_SIZE

NGX_HTTP_LOCK

NGX_HTTP_LOG_UNSAFE

NGX_HTTP_LOWLEVEL_BUFFERED

NGX_HTTP_MAX_SUBREQUESTS

NGX_HTTP_MAX_URI_CHANGES

NGX_HTTP_MKCOL

NGX_HTTP_MOVE

NGX_HTTP_MOVED_PERMANENTLY

NGX_HTTP_MOVED_TEMPORARILY

NGX_HTTP_NGINX_CODES

NGX_HTTP_NOT_ALLOWED

NGX_HTTP_NOT_FOUND

NGX_HTTP_NOT_IMPLEMENTED

NGX_HTTP_NOT_MODIFIED

NGX_HTTP_NO_CONTENT

NGX_HTTP_OK

NGX_HTTP_OPTIONS

NGX_HTTP_PARSE_HEADER_DONE

NGX_HTTP_PARSE_INVALID_09_METHOD

NGX_HTTP_PARSE_INVALID_HEADER

NGX_HTTP_PARSE_INVALID_METHOD

NGX_HTTP_PARSE_INVALID_REQUEST

NGX_HTTP_PARTIAL_CONTENT

NGX_HTTP_PATCH

NGX_HTTP_POST

NGX_HTTP_PRECONDITION_FAILED

NGX_HTTP_PROCESSING

NGX_HTTP_PROPFIND

NGX_HTTP_PROPPATCH

NGX_HTTP_PUT

NGX_HTTP_RANGE_NOT_SATISFIABLE

NGX_HTTP_REQUEST_ENTITY_TOO_LARGE

NGX_HTTP_REQUEST_HEADER_TOO_LARGE

NGX_HTTP_REQUEST_TIME_OUT

NGX_HTTP_REQUEST_URI_TOO_LARGE

NGX_HTTP_SEE_OTHER

NGX_HTTP_SERVICE_UNAVAILABLE

NGX_HTTP_SPECIAL_RESPONSE

NGX_HTTP_SSI_BUFFERED

NGX_HTTP_SUBREQUEST_IN_MEMORY

NGX_HTTP_SUBREQUEST_WAITED

NGX_HTTP_SUB_BUFFERED

NGX_HTTP_SWITCHING_PROTOCOLS

NGX_HTTP_TEMPORARY_REDIRECT

NGX_HTTP_TO_HTTPS

NGX_HTTP_TRACE

NGX_HTTP_UNAUTHORIZED

NGX_HTTP_UNKNOWN

NGX_HTTP_UNLOCK

NGX_HTTP_UNSUPPORTED_MEDIA_TYPE

NGX_HTTP_VERSION_10

NGX_HTTP_VERSION_11

NGX_HTTP_VERSION_9

NGX_HTTP_WRITE_BUFFERED

NGX_NONE

_NGX_HTTP_REQUEST_H_INCLUDED_

ngx_http_set_connection_log

ngx_http_set_log_request

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_

‌#define _NGX_HTTP_REQUEST_H_INCLUDED_

‌#define NGX_HTTP_MAX_URI_CHANGES           10

‌#define NGX_HTTP_MAX_SUBREQUESTS           200

/* must be 2^n */

‌#define NGX_HTTP_LC_HEADER_LEN             32

‌#define NGX_HTTP_DISCARD_BUFFER_SIZE       4096

‌#define NGX_HTTP_LINGERING_BUFFER_SIZE     4096

‌#define NGX_HTTP_VERSION_9                 9

‌#define NGX_HTTP_VERSION_10                1000

‌#define NGX_HTTP_VERSION_11                1001

‌#define NGX_HTTP_UNKNOWN                   0x0001

‌#define NGX_HTTP_GET                       0x0002

‌#define NGX_HTTP_HEAD                      0x0004

‌#define NGX_HTTP_POST                      0x0008

‌#define NGX_HTTP_PUT                       0x0010

‌#define NGX_HTTP_DELETE                    0x0020

‌#define NGX_HTTP_MKCOL                     0x0040

‌#define NGX_HTTP_COPY                      0x0080

‌#define NGX_HTTP_MOVE                      0x0100

‌#define NGX_HTTP_OPTIONS                   0x0200

‌#define NGX_HTTP_PROPFIND                  0x0400

‌#define NGX_HTTP_PROPPATCH                 0x0800

‌#define NGX_HTTP_LOCK                      0x1000

‌#define NGX_HTTP_UNLOCK                    0x2000

‌#define NGX_HTTP_PATCH                     0x4000

‌#define NGX_HTTP_TRACE                     0x8000

‌#define NGX_HTTP_CONNECTION_CLOSE          1

‌#define NGX_HTTP_CONNECTION_KEEP_ALIVE     2

‌#define NGX_NONE                           1

‌#define NGX_HTTP_PARSE_HEADER_DONE         1

‌#define NGX_HTTP_CLIENT_ERROR              10

‌#define NGX_HTTP_PARSE_INVALID_METHOD      10

‌#define NGX_HTTP_PARSE_INVALID_REQUEST     11

‌#define NGX_HTTP_PARSE_INVALID_09_METHOD   12

‌#define NGX_HTTP_PARSE_INVALID_HEADER      13

/* unused                                  1 */

‌#define NGX_HTTP_SUBREQUEST_IN_MEMORY      2

‌#define NGX_HTTP_SUBREQUEST_WAITED         4

‌#define NGX_HTTP_LOG_UNSAFE                8

‌#define NGX_HTTP_CONTINUE                  100

‌#define NGX_HTTP_SWITCHING_PROTOCOLS       101

‌#define NGX_HTTP_PROCESSING                102

‌#define NGX_HTTP_OK                        200

‌#define NGX_HTTP_CREATED                   201

‌#define NGX_HTTP_ACCEPTED                  202

‌#define NGX_HTTP_NO_CONTENT                204

‌#define NGX_HTTP_PARTIAL_CONTENT           206

‌#define NGX_HTTP_SPECIAL_RESPONSE          300

‌#define NGX_HTTP_MOVED_PERMANENTLY         301

‌#define NGX_HTTP_MOVED_TEMPORARILY         302

‌#define NGX_HTTP_SEE_OTHER                 303

‌#define NGX_HTTP_NOT_MODIFIED              304

‌#define NGX_HTTP_TEMPORARY_REDIRECT        307

‌#define NGX_HTTP_BAD_REQUEST               400

‌#define NGX_HTTP_UNAUTHORIZED              401

‌#define NGX_HTTP_FORBIDDEN                 403

‌#define NGX_HTTP_NOT_FOUND                 404

‌#define NGX_HTTP_NOT_ALLOWED               405

‌#define NGX_HTTP_REQUEST_TIME_OUT          408

‌#define NGX_HTTP_CONFLICT                  409

‌#define NGX_HTTP_LENGTH_REQUIRED           411

‌#define NGX_HTTP_PRECONDITION_FAILED       412

‌#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE  413

‌#define NGX_HTTP_REQUEST_URI_TOO_LARGE     414

‌#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE    415

‌#define NGX_HTTP_RANGE_NOT_SATISFIABLE     416

/* Our own HTTP codes */

/* The special code to close connection without any response */

‌#define NGX_HTTP_CLOSE                     444

‌#define NGX_HTTP_NGINX_CODES               494

‌#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE  494

‌#define NGX_HTTPS_CERT_ERROR               495

‌#define NGX_HTTPS_NO_CERT                  496

/*

* We use the special code for the plain HTTP requests that are sent to

* HTTPS port to distinguish it from 4XX in an error page redirection

*/

‌#define NGX_HTTP_TO_HTTPS                  497

/* 498 is the canceled code for the requests with invalid host name */

/*

* HTTP does not define the code for the case when a client closed

* the connection while we are processing its request so we introduce

* own code to log such situation when a client has closed the connection

* before we even try to send the HTTP header to it

*/

‌#define NGX_HTTP_CLIENT_CLOSED_REQUEST     499

‌#define NGX_HTTP_INTERNAL_SERVER_ERROR     500

‌#define NGX_HTTP_NOT_IMPLEMENTED           501

‌#define NGX_HTTP_BAD_GATEWAY               502

‌#define NGX_HTTP_SERVICE_UNAVAILABLE       503

‌#define NGX_HTTP_GATEWAY_TIME_OUT          504

‌#define NGX_HTTP_INSUFFICIENT_STORAGE      507

‌#define NGX_HTTP_LOWLEVEL_BUFFERED         0xf0

‌#define NGX_HTTP_WRITE_BUFFERED            0x10

‌#define NGX_HTTP_GZIP_BUFFERED             0x20

‌#define NGX_HTTP_SSI_BUFFERED              0x01

‌#define NGX_HTTP_SUB_BUFFERED              0x02

‌#define NGX_HTTP_COPY_BUFFERED             0x04

typedef enum {

    NGX_HTTP_INITING_REQUEST_STATE = 0,

    NGX_HTTP_READING_REQUEST_STATE,

    NGX_HTTP_PROCESS_REQUEST_STATE,

    NGX_HTTP_CONNECT_UPSTREAM_STATE,

    NGX_HTTP_WRITING_UPSTREAM_STATE,

    NGX_HTTP_READING_UPSTREAM_STATE,

    NGX_HTTP_WRITING_REQUEST_STATE,

    NGX_HTTP_LINGERING_CLOSE_STATE,

    NGX_HTTP_KEEPALIVE_STATE

‌} ngx_http_state_e;

typedef struct {

    ngx_str_t                         name;

    ngx_uint_t                        offset;

    ngx_http_header_handler_pt        handler;

‌} ngx_http_header_t;

typedef struct {

    ngx_str_t                         name;

    ngx_uint_t                        offset;

‌} ngx_http_header_out_t;

typedef struct {

    ngx_list_t                        headers;

    ngx_table_elt_t                  *host;

    ngx_table_elt_t                  *connection;

    ngx_table_elt_t                  *if_modified_since;

    ngx_table_elt_t                  *if_unmodified_since;

    ngx_table_elt_t                  *if_match;

    ngx_table_elt_t                  *if_none_match;

    ngx_table_elt_t                  *user_agent;

    ngx_table_elt_t                  *referer;

    ngx_table_elt_t                  *content_length;

    ngx_table_elt_t                  *content_type;

    ngx_table_elt_t                  *range;

    ngx_table_elt_t                  *if_range;

    ngx_table_elt_t                  *transfer_encoding;

    ngx_table_elt_t                  *expect;

    ngx_table_elt_t                  *upgrade;

#if (NGX_HTTP_GZIP)

    ngx_table_elt_t                  *accept_encoding;

    ngx_table_elt_t                  *via;

#endif

    ngx_table_elt_t                  *authorization;

    ngx_table_elt_t                  *keep_alive;

#if (NGX_HTTP_X_FORWARDED_FOR)

    ngx_array_t                       x_forwarded_for;

#endif

#if (NGX_HTTP_REALIP)

    ngx_table_elt_t                  *x_real_ip;

#endif

#if (NGX_HTTP_HEADERS)

    ngx_table_elt_t                  *accept;

    ngx_table_elt_t                  *accept_language;

#endif

#if (NGX_HTTP_DAV)

    ngx_table_elt_t                  *depth;

    ngx_table_elt_t                  *destination;

    ngx_table_elt_t                  *overwrite;

    ngx_table_elt_t                  *date;

#endif

    ngx_str_t                         user;

    ngx_str_t                         passwd;

    ngx_array_t                       cookies;

    ngx_str_t                         server;

    off_t                             content_length_n;

    time_t                            keep_alive_n;

    unsigned                          connection_type:2;

    unsigned                          chunked:1;

    unsigned                          msie:1;

    unsigned                          msie6:1;

    unsigned                          opera:1;

    unsigned                          gecko:1;

    unsigned                          chrome:1;

    unsigned                          safari:1;

    unsigned                          konqueror:1;

‌} ngx_http_headers_in_t;

typedef struct {

    ngx_list_t                        headers;

    ngx_uint_t                        status;

    ngx_str_t                         status_line;

    ngx_table_elt_t                  *server;

    ngx_table_elt_t                  *date;

    ngx_table_elt_t                  *content_length;

    ngx_table_elt_t                  *content_encoding;

    ngx_table_elt_t                  *location;

    ngx_table_elt_t                  *refresh;

    ngx_table_elt_t                  *last_modified;

    ngx_table_elt_t                  *content_range;

    ngx_table_elt_t                  *accept_ranges;

    ngx_table_elt_t                  *www_authenticate;

    ngx_table_elt_t                  *expires;

    ngx_table_elt_t                  *etag;

    ngx_str_t                        *override_charset;

    size_t                            content_type_len;

    ngx_str_t                         content_type;

    ngx_str_t                         charset;

    u_char                           *content_type_lowcase;

    ngx_uint_t                        content_type_hash;

    ngx_array_t                       cache_control;

    off_t                             content_length_n;

    time_t                            date_time;

    time_t                            last_modified_time;

‌} ngx_http_headers_out_t;

‌typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);

typedef struct {

    ngx_temp_file_t                  *temp_file;

    ngx_chain_t                      *bufs;

    ngx_buf_t                        *buf;

    off_t                             rest;

    ngx_chain_t                      *free;

    ngx_chain_t                      *busy;

    ngx_http_chunked_t               *chunked;

    ngx_http_client_body_handler_pt   post_handler;

‌} ngx_http_request_body_t;

‌typedef struct ngx_http_addr_conf_s  ngx_http_addr_conf_t;

typedef struct {

    ngx_http_addr_conf_t             *addr_conf;

    ngx_http_conf_ctx_t              *conf_ctx;

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

    ngx_str_t                        *ssl_servername;

#if (NGX_PCRE)

    ngx_http_regex_t                 *ssl_servername_regex;

#endif

#endif

    ngx_buf_t                       **busy;

    ngx_int_t                         nbusy;

    ngx_buf_t                       **free;

    ngx_int_t                         nfree;

#if (NGX_HTTP_SSL)

    unsigned                          ssl:1;

#endif

    unsigned                          proxy_protocol:1;

‌} ngx_http_connection_t;

‌typedef void (*ngx_http_cleanup_pt)(void *data);

‌typedef struct ngx_http_cleanup_s  ngx_http_cleanup_t;

‌struct ngx_http_cleanup_s {

    ngx_http_cleanup_pt               handler;

    void                             *data;

    ngx_http_cleanup_t               *next;

};

‌typedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,

    void *data, ngx_int_t rc);

typedef struct {

    ngx_http_post_subrequest_pt       handler;

    void                             *data;

‌} ngx_http_post_subrequest_t;

‌typedef struct ngx_http_postponed_request_s  ngx_http_postponed_request_t;

‌struct ngx_http_postponed_request_s {

    ngx_http_request_t               *request;

    ngx_chain_t                      *out;

    ngx_http_postponed_request_t     *next;

};

‌typedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;

‌struct ngx_http_posted_request_s {

    ngx_http_request_t               *request;

    ngx_http_posted_request_t        *next;

};

‌typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

‌typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);

‌struct ngx_http_request_s {

    uint32_t                          signature;         /* "HTTP" */

    ngx_connection_t                 *connection;

    void                            **ctx;

    void                            **main_conf;

    void                            **srv_conf;

    void                            **loc_conf;

    ngx_http_event_handler_pt         read_event_handler;

    ngx_http_event_handler_pt         write_event_handler;

#if (NGX_HTTP_CACHE)

    ngx_http_cache_t                 *cache;

#endif

    ngx_http_upstream_t              *upstream;

    ngx_array_t                      *upstream_states;

                                         /* of ngx_http_upstream_state_t */

    ngx_pool_t                       *pool;

    ngx_buf_t                        *header_in;

    ngx_http_headers_in_t             headers_in;

    ngx_http_headers_out_t            headers_out;

    ngx_http_request_body_t          *request_body;

    time_t                            lingering_time;

    time_t                            start_sec;

    ngx_msec_t                        start_msec;

    ngx_uint_t                        method;

    ngx_uint_t                        http_version;

    ngx_str_t                         request_line;

    ngx_str_t                         uri;

    ngx_str_t                         args;

    ngx_str_t                         exten;

    ngx_str_t                         unparsed_uri;

    ngx_str_t                         method_name;

    ngx_str_t                         http_protocol;

    ngx_chain_t                      *out;

    ngx_http_request_t               *main;

    ngx_http_request_t               *parent;

    ngx_http_postponed_request_t     *postponed;

    ngx_http_post_subrequest_t       *post_subrequest;

    ngx_http_posted_request_t        *posted_requests;

    ngx_int_t                         phase_handler;

    ngx_http_handler_pt               content_handler;

    ngx_uint_t                        access_code;

    ngx_http_variable_value_t        *variables;

#if (NGX_PCRE)

    ngx_uint_t                        ncaptures;

    int                              *captures;

    u_char                           *captures_data;

#endif

    size_t                            limit_rate;

    size_t                            limit_rate_after;

    /* used to learn the Apache compatible response length without a header */

    size_t                            header_size;

    off_t                             request_length;

    ngx_uint_t                        err_status;

    ngx_http_connection_t            *http_connection;

#if (NGX_HTTP_SPDY)

    ngx_http_spdy_stream_t           *spdy_stream;

#endif

    ngx_http_log_handler_pt           log_handler;

    ngx_http_cleanup_t               *cleanup;

    unsigned                          subrequests:8;

    unsigned                          count:8;

    unsigned                          blocked:8;

    unsigned                          aio:1;

    unsigned                          http_state:4;

    /* URI with "/." and on Win32 with "//" */

    unsigned                          complex_uri:1;

    /* URI with "%" */

    unsigned                          quoted_uri:1;

    /* URI with "+" */

    unsigned                          plus_in_uri:1;

    /* URI with " " */

    unsigned                          space_in_uri:1;

    unsigned                          invalid_header:1;

    unsigned                          add_uri_to_alias:1;

    unsigned                          valid_location:1;

    unsigned                          valid_unparsed_uri:1;

    unsigned                          uri_changed:1;

    unsigned                          uri_changes:4;

    unsigned                          request_body_in_single_buf:1;

    unsigned                          request_body_in_file_only:1;

    unsigned                          request_body_in_persistent_file:1;

    unsigned                          request_body_in_clean_file:1;

    unsigned                          request_body_file_group_access:1;

    unsigned                          request_body_file_log_level:3;

    unsigned                          subrequest_in_memory:1;

    unsigned                          waited:1;

#if (NGX_HTTP_CACHE)

    unsigned                          cached:1;

#endif

#if (NGX_HTTP_GZIP)

    unsigned                          gzip_tested:1;

    unsigned                          gzip_ok:1;

    unsigned                          gzip_vary:1;

#endif

    unsigned                          proxy:1;

    unsigned                          bypass_cache:1;

    unsigned                          no_cache:1;

    /*

     * instead of using the request context data in

     * ngx_http_limit_conn_module and ngx_http_limit_req_module

     * we use the single bits in the request structure

     */

    unsigned                          limit_conn_set:1;

    unsigned                          limit_req_set:1;

#if 0

    unsigned                          cacheable:1;

#endif

    unsigned                          pipeline:1;

    unsigned                          chunked:1;

    unsigned                          header_only:1;

    unsigned                          keepalive:1;

    unsigned                          lingering_close:1;

    unsigned                          discard_body:1;

    unsigned                          internal:1;

    unsigned                          error_page:1;

    unsigned                          filter_finalize:1;

    unsigned                          post_action:1;

    unsigned                          request_complete:1;

    unsigned                          request_output:1;

    unsigned                          header_sent:1;

    unsigned                          expect_tested:1;

    unsigned                          root_tested:1;

    unsigned                          done:1;

    unsigned                          logged:1;

    unsigned                          buffered:4;

    unsigned                          main_filter_need_in_memory:1;

    unsigned                          filter_need_in_memory:1;

    unsigned                          filter_need_temporary:1;

    unsigned                          allow_ranges:1;

    unsigned                          single_range:1;

    unsigned                          disable_not_modified:1;

#if (NGX_STAT_STUB)

    unsigned                          stat_reading:1;

    unsigned                          stat_writing:1;

#endif

    /* used to parse HTTP headers */

    ngx_uint_t                        state;

    ngx_uint_t                        header_hash;

    ngx_uint_t                        lowcase_index;

    u_char                            lowcase_header[NGX_HTTP_LC_HEADER_LEN];

    u_char                           *header_name_start;

    u_char                           *header_name_end;

    u_char                           *header_start;

    u_char                           *header_end;

    /*

     * a memory that can be reused after parsing a request line

     * via ngx_http_ephemeral_t

     */

    u_char                           *uri_start;

    u_char                           *uri_end;

    u_char                           *uri_ext;

    u_char                           *args_start;

    u_char                           *request_start;

    u_char                           *request_end;

    u_char                           *method_end;

    u_char                           *schema_start;

    u_char                           *schema_end;

    u_char                           *host_start;

    u_char                           *host_end;

    u_char                           *port_start;

    u_char                           *port_end;

    unsigned                          http_minor:16;

    unsigned                          http_major:16;

};

typedef struct {

    ngx_http_posted_request_t         terminal_posted_request;

#if (NGX_HAVE_AIO_SENDFILE)

    u_char                            aio_preload;

#endif

‌} ngx_http_ephemeral_t;

extern ngx_http_header_t       ngx_http_headers_in[];

extern ngx_http_header_out_t   ngx_http_headers_out[];

‌#define ngx_http_set_connection_log(c, l)                                     \

                                                                              \

    c->log->file = l->file;                                                   \

    c->log->next = l->next;                                                   \

    c->log->writer = l->writer;                                               \

    c->log->wdata = l->wdata;                                                 \

    if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {                    \

        c->log->log_level = l->log_level;                                     \

    }

‌#define ngx_http_set_log_request(log, r)                                      \

    ((ngx_http_log_ctx_t *) log->data)->current_request = r

#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */

src/core/ngx_list.h - nginx-1.7.9

Data types defined

ngx_list_part_s

ngx_list_part_t

ngx_list_t

Functions defined

ngx_list_init

Macros defined

_NGX_LIST_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_LIST_H_INCLUDED_

‌#define _NGX_LIST_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef struct ngx_list_part_s  ngx_list_part_t;

‌struct ngx_list_part_s {

    void             *elts;

    ngx_uint_t        nelts;

    ngx_list_part_t  *next;

};

typedef struct {

    ngx_list_part_t  *last;

    ngx_list_part_t   part;

    size_t            size;

    ngx_uint_t        nalloc;

    ngx_pool_t       *pool;

‌} ngx_list_t;

ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);

static ngx_inline ngx_int_t

‌ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)

{

    list->part.elts = ngx_palloc(pool, n * size);

    if (list->part.elts == NULL) {

        return NGX_ERROR;

    }

    list->part.nelts = 0;

    list->part.next = NULL;

    list->last = &list->part;

    list->size = size;

    list->nalloc = n;

    list->pool = pool;

    return NGX_OK;

}

/*

*

*  the iteration through the list:

*

*  part = &list.part;

*  data = part->elts;

*

*  for (i = 0 ;; i++) {

*

*      if (i >= part->nelts) {

*          if (part->next == NULL) {

*              break;

*          }

*

*          part = part->next;

*          data = part->elts;

*          i = 0;

*      }

*

*      ...  data[i] ...

*

*  }

*/

void *ngx_list_push(ngx_list_t *list);

#endif /* _NGX_LIST_H_INCLUDED_ */

src/core/ngx_list.c - nginx-1.7.9

Functions defined

ngx_list_create

ngx_list_push

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

ngx_list_t *

‌ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)

{

    ngx_list_t  *list;

    list = ngx_palloc(pool, sizeof(ngx_list_t));

    if (list == NULL) {

        return NULL;

    }

    if (ngx_list_init(list, pool, n, size) != NGX_OK) {

        return NULL;

    }

    return list;

}

void *

‌ngx_list_push(ngx_list_t *l)

{

    void             *elt;

    ngx_list_part_t  *last;

    last = l->last;

    if (last->nelts == l->nalloc) {

        /* the last part is full, allocate a new list part */

        last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));

        if (last == NULL) {

            return NULL;

        }

        last->elts = ngx_palloc(l->pool, l->nalloc * l->size);

        if (last->elts == NULL) {

            return NULL;

        }

        last->nelts = 0;

        last->next = NULL;

        l->last->next = last;

        l->last = last;

    }

    elt = (char *) last->elts + l->size * last->nelts;

    last->nelts++;

    return elt;

}

src/core/ngx_hash.h - nginx-1.7.9

Data types defined

ngx_hash_combined_t

ngx_hash_elt_t

ngx_hash_init_t

ngx_hash_key_pt

ngx_hash_key_t

ngx_hash_keys_arrays_t

ngx_hash_t

ngx_hash_wildcard_t

ngx_table_elt_t

Macros defined

NGX_HASH_LARGE

NGX_HASH_LARGE_ASIZE

NGX_HASH_LARGE_HSIZE

NGX_HASH_READONLY_KEY

NGX_HASH_SMALL

NGX_HASH_WILDCARD_KEY

_NGX_HASH_H_INCLUDED_

ngx_hash

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HASH_H_INCLUDED_

‌#define _NGX_HASH_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    void             *value;

    u_short           len;

    u_char            name[1];

‌} ngx_hash_elt_t;

typedef struct {

    ngx_hash_elt_t  **buckets;

    ngx_uint_t        size;

‌} ngx_hash_t;

typedef struct {

    ngx_hash_t        hash;

    void             *value;

‌} ngx_hash_wildcard_t;

typedef struct {

    ngx_str_t         key;

    ngx_uint_t        key_hash;

    void             *value;

‌} ngx_hash_key_t;

‌typedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len);

typedef struct {

    ngx_hash_t            hash;

    ngx_hash_wildcard_t  *wc_head;

    ngx_hash_wildcard_t  *wc_tail;

‌} ngx_hash_combined_t;

typedef struct {

    ngx_hash_t       *hash;

    ngx_hash_key_pt   key;

    ngx_uint_t        max_size;

    ngx_uint_t        bucket_size;

    char             *name;

    ngx_pool_t       *pool;

    ngx_pool_t       *temp_pool;

‌} ngx_hash_init_t;

‌#define NGX_HASH_SMALL            1

‌#define NGX_HASH_LARGE            2

‌#define NGX_HASH_LARGE_ASIZE      16384

‌#define NGX_HASH_LARGE_HSIZE      10007

‌#define NGX_HASH_WILDCARD_KEY     1

‌#define NGX_HASH_READONLY_KEY     2

typedef struct {

    ngx_uint_t        hsize;

    ngx_pool_t       *pool;

    ngx_pool_t       *temp_pool;

    ngx_array_t       keys;

    ngx_array_t      *keys_hash;

    ngx_array_t       dns_wc_head;

    ngx_array_t      *dns_wc_head_hash;

    ngx_array_t       dns_wc_tail;

    ngx_array_t      *dns_wc_tail_hash;

‌} ngx_hash_keys_arrays_t;

typedef struct {

    ngx_uint_t        hash;

    ngx_str_t         key;

    ngx_str_t         value;

    u_char           *lowcase_key;

‌} ngx_table_elt_t;

void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);

void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);

void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);

void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key,

    u_char *name, size_t len);

ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,

    ngx_uint_t nelts);

ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,

    ngx_uint_t nelts);

‌#define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)

ngx_uint_t ngx_hash_key(u_char *data, size_t len);

ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);

ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n);

ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);

ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key,

    void *value, ngx_uint_t flags);

#endif /* _NGX_HASH_H_INCLUDED_ */

src/core/ngx_hash.c - nginx-1.7.9

Functions defined

ngx_hash_add_key

ngx_hash_find

ngx_hash_find_combined

ngx_hash_find_wc_head

ngx_hash_find_wc_tail

ngx_hash_init

ngx_hash_key

ngx_hash_key_lc

ngx_hash_keys_array_init

ngx_hash_strlow

ngx_hash_wildcard_init

Macros defined

NGX_HASH_ELT_SIZE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

void *

‌ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)

{

    ngx_uint_t       i;

    ngx_hash_elt_t  *elt;

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name);

#endif

    elt = hash->buckets[key % hash->size];

    if (elt == NULL) {

        return NULL;

    }

    while (elt->value) {

        if (len != (size_t) elt->len) {

            goto next;

        }

        for (i = 0; i < len; i++) {

            if (name[i] != elt->name[i]) {

                goto next;

            }

        }

        return elt->value;

    next:

        elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,

                                               sizeof(void *));

        continue;

    }

    return NULL;

}

void *

‌ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)

{

    void        *value;

    ngx_uint_t   i, n, key;

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%*s\"", len, name);

#endif

    n = len;

    while (n) {

        if (name[n - 1] == '.') {

            break;

        }

        n--;

    }

    key = 0;

    for (i = n; i < len; i++) {

        key = ngx_hash(key, name[i]);

    }

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);

#endif

    value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);

#endif

    if (value) {

        /*

         * the 2 low bits of value have the special meaning:

         *     00 - value is data pointer for both "example.com"

         *          and "*.example.com";

         *     01 - value is data pointer for "*.example.com" only;

         *     10 - value is pointer to wildcard hash allowing

         *          both "example.com" and "*.example.com";

         *     11 - value is pointer to wildcard hash allowing

         *          "*.example.com" only.

         */

        if ((uintptr_t) value & 2) {

            if (n == 0) {

                /* "example.com" */

                if ((uintptr_t) value & 1) {

                    return NULL;

                }

                hwc = (ngx_hash_wildcard_t *)

                                          ((uintptr_t) value & (uintptr_t) ~3);

                return hwc->value;

            }

            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);

            value = ngx_hash_find_wc_head(hwc, name, n - 1);

            if (value) {

                return value;

            }

            return hwc->value;

        }

        if ((uintptr_t) value & 1) {

            if (n == 0) {

                /* "example.com" */

                return NULL;

            }

            return (void *) ((uintptr_t) value & (uintptr_t) ~3);

        }

        return value;

    }

    return hwc->value;

}

void *

‌ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)

{

    void        *value;

    ngx_uint_t   i, key;

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%*s\"", len, name);

#endif

    key = 0;

    for (i = 0; i < len; i++) {

        if (name[i] == '.') {

            break;

        }

        key = ngx_hash(key, name[i]);

    }

    if (i == len) {

        return NULL;

    }

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);

#endif

    value = ngx_hash_find(&hwc->hash, key, name, i);

#if 0

    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);

#endif

    if (value) {

        /*

         * the 2 low bits of value have the special meaning:

         *     00 - value is data pointer;

         *     11 - value is pointer to wildcard hash allowing "example.*".

         */

        if ((uintptr_t) value & 2) {

            i++;

            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);

            value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);

            if (value) {

                return value;

            }

            return hwc->value;

        }

        return value;

    }

    return hwc->value;

}

void *

‌ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,

    size_t len)

{

    void  *value;

    if (hash->hash.buckets) {

        value = ngx_hash_find(&hash->hash, key, name, len);

        if (value) {

            return value;

        }

    }

    if (len == 0) {

        return NULL;

    }

    if (hash->wc_head && hash->wc_head->hash.buckets) {

        value = ngx_hash_find_wc_head(hash->wc_head, name, len);

        if (value) {

            return value;

        }

    }

    if (hash->wc_tail && hash->wc_tail->hash.buckets) {

        value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);

        if (value) {

            return value;

        }

    }

    return NULL;

}

‌#define NGX_HASH_ELT_SIZE(name)                                               \

    (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))

ngx_int_t

‌ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)

{

    u_char          *elts;

    size_t           len;

    u_short         *test;

    ngx_uint_t       i, n, key, size, start, bucket_size;

    ngx_hash_elt_t  *elt, **buckets;

    for (n = 0; n < nelts; n++) {

        if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))

        {

            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,

                          "could not build the %s, you should "

                          "increase %s_bucket_size: %i",

                          hinit->name, hinit->name, hinit->bucket_size);

            return NGX_ERROR;

        }

    }

    test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);

    if (test == NULL) {

        return NGX_ERROR;

    }

    bucket_size = hinit->bucket_size - sizeof(void *);

    start = nelts / (bucket_size / (2 * sizeof(void *)));

    start = start ? start : 1;

    if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {

        start = hinit->max_size - 1000;

    }

    for (size = start; size <= hinit->max_size; size++) {

        ngx_memzero(test, size * sizeof(u_short));

        for (n = 0; n < nelts; n++) {

            if (names[n].key.data == NULL) {

                continue;

            }

            key = names[n].key_hash % size;

            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));

#if 0

            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                          "%ui: %ui %ui \"%V\"",

                          size, key, test[key], &names[n].key);

#endif

            if (test[key] > (u_short) bucket_size) {

                goto next;

            }

        }

        goto found;

    next:

        continue;

    }

    size--;

    ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0,

                  "could not build optimal %s, you should increase "

                  "either %s_max_size: %i or %s_bucket_size: %i; "

                  "ignoring %s_bucket_size",

                  hinit->name, hinit->name, hinit->max_size,

                  hinit->name, hinit->bucket_size, hinit->name);

found:

    for (i = 0; i < size; i++) {

        test[i] = sizeof(void *);

    }

    for (n = 0; n < nelts; n++) {

        if (names[n].key.data == NULL) {

            continue;

        }

        key = names[n].key_hash % size;

        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));

    }

    len = 0;

    for (i = 0; i < size; i++) {

        if (test[i] == sizeof(void *)) {

            continue;

        }

        test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));

        len += test[i];

    }

    if (hinit->hash == NULL) {

        hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)

                                             + size * sizeof(ngx_hash_elt_t *));

        if (hinit->hash == NULL) {

            ngx_free(test);

            return NGX_ERROR;

        }

        buckets = (ngx_hash_elt_t **)

                      ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));

    } else {

        buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));

        if (buckets == NULL) {

            ngx_free(test);

            return NGX_ERROR;

        }

    }

    elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);

    if (elts == NULL) {

        ngx_free(test);

        return NGX_ERROR;

    }

    elts = ngx_align_ptr(elts, ngx_cacheline_size);

    for (i = 0; i < size; i++) {

        if (test[i] == sizeof(void *)) {

            continue;

        }

        buckets[i] = (ngx_hash_elt_t *) elts;

        elts += test[i];

    }

    for (i = 0; i < size; i++) {

        test[i] = 0;

    }

    for (n = 0; n < nelts; n++) {

        if (names[n].key.data == NULL) {

            continue;

        }

        key = names[n].key_hash % size;

        elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);

        elt->value = names[n].value;

        elt->len = (u_short) names[n].key.len;

        ngx_strlow(elt->name, names[n].key.data, names[n].key.len);

        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));

    }

    for (i = 0; i < size; i++) {

        if (buckets[i] == NULL) {

            continue;

        }

        elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);

        elt->value = NULL;

    }

    ngx_free(test);

    hinit->hash->buckets = buckets;

    hinit->hash->size = size;

#if 0

    for (i = 0; i < size; i++) {

        ngx_str_t   val;

        ngx_uint_t  key;

        elt = buckets[i];

        if (elt == NULL) {

            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                          "%ui: NULL", i);

            continue;

        }

        while (elt->value) {

            val.len = elt->len;

            val.data = &elt->name[0];

            key = hinit->key(val.data, val.len);

            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                          "%ui: %p \"%V\" %ui", i, elt, &val, key);

            elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,

                                                   sizeof(void *));

        }

    }

#endif

    return NGX_OK;

}

ngx_int_t

‌ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,

    ngx_uint_t nelts)

{

    size_t                len, dot_len;

    ngx_uint_t            i, n, dot;

    ngx_array_t           curr_names, next_names;

    ngx_hash_key_t       *name, *next_name;

    ngx_hash_init_t       h;

    ngx_hash_wildcard_t  *wdc;

    if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,

                       sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&next_names, hinit->temp_pool, nelts,

                       sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    for (n = 0; n < nelts; n = i) {

#if 0

        ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                      "wc0: \"%V\"", &names[n].key);

#endif

        dot = 0;

        for (len = 0; len < names[n].key.len; len++) {

            if (names[n].key.data[len] == '.') {

                dot = 1;

                break;

            }

        }

        name = ngx_array_push(&curr_names);

        if (name == NULL) {

            return NGX_ERROR;

        }

        name->key.len = len;

        name->key.data = names[n].key.data;

        name->key_hash = hinit->key(name->key.data, name->key.len);

        name->value = names[n].value;

#if 0

        ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                      "wc1: \"%V\" %ui", &name->key, dot);

#endif

        dot_len = len + 1;

        if (dot) {

            len++;

        }

        next_names.nelts = 0;

        if (names[n].key.len != len) {

            next_name = ngx_array_push(&next_names);

            if (next_name == NULL) {

                return NGX_ERROR;

            }

            next_name->key.len = names[n].key.len - len;

            next_name->key.data = names[n].key.data + len;

            next_name->key_hash = 0;

            next_name->value = names[n].value;

#if 0

            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                          "wc2: \"%V\"", &next_name->key);

#endif

        }

        for (i = n + 1; i < nelts; i++) {

            if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {

                break;

            }

            if (!dot

                && names[i].key.len > len

                && names[i].key.data[len] != '.')

            {

                break;

            }

            next_name = ngx_array_push(&next_names);

            if (next_name == NULL) {

                return NGX_ERROR;

            }

            next_name->key.len = names[i].key.len - dot_len;

            next_name->key.data = names[i].key.data + dot_len;

            next_name->key_hash = 0;

            next_name->value = names[i].value;

#if 0

            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,

                          "wc3: \"%V\"", &next_name->key);

#endif

        }

        if (next_names.nelts) {

            h = *hinit;

            h.hash = NULL;

            if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,

                                       next_names.nelts)

                != NGX_OK)

            {

                return NGX_ERROR;

            }

            wdc = (ngx_hash_wildcard_t *) h.hash;

            if (names[n].key.len == len) {

                wdc->value = names[n].value;

            }

            name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));

        } else if (dot) {

            name->value = (void *) ((uintptr_t) name->value | 1);

        }

    }

    if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,

                      curr_names.nelts)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_uint_t

‌ngx_hash_key(u_char *data, size_t len)

{

    ngx_uint_t  i, key;

    key = 0;

    for (i = 0; i < len; i++) {

        key = ngx_hash(key, data[i]);

    }

    return key;

}

ngx_uint_t

‌ngx_hash_key_lc(u_char *data, size_t len)

{

    ngx_uint_t  i, key;

    key = 0;

    for (i = 0; i < len; i++) {

        key = ngx_hash(key, ngx_tolower(data[i]));

    }

    return key;

}

ngx_uint_t

‌ngx_hash_strlow(u_char *dst, u_char *src, size_t n)

{

    ngx_uint_t  key;

    key = 0;

    while (n--) {

        *dst = ngx_tolower(*src);

        key = ngx_hash(key, *dst);

        dst++;

        src++;

    }

    return key;

}

ngx_int_t

‌ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)

{

    ngx_uint_t  asize;

    if (type == NGX_HASH_SMALL) {

        asize = 4;

        ha->hsize = 107;

    } else {

        asize = NGX_HASH_LARGE_ASIZE;

        ha->hsize = NGX_HASH_LARGE_HSIZE;

    }

    if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,

                       sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,

                       sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);

    if (ha->keys_hash == NULL) {

        return NGX_ERROR;

    }

    ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,

                                       sizeof(ngx_array_t) * ha->hsize);

    if (ha->dns_wc_head_hash == NULL) {

        return NGX_ERROR;

    }

    ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,

                                       sizeof(ngx_array_t) * ha->hsize);

    if (ha->dns_wc_tail_hash == NULL) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,

    ngx_uint_t flags)

{

    size_t           len;

    u_char          *p;

    ngx_str_t       *name;

    ngx_uint_t       i, k, n, skip, last;

    ngx_array_t     *keys, *hwc;

    ngx_hash_key_t  *hk;

    last = key->len;

    if (flags & NGX_HASH_WILDCARD_KEY) {

        /*

         * supported wildcards:

         *     "*.example.com", ".example.com", and "www.example.*"

         */

        n = 0;

        for (i = 0; i < key->len; i++) {

            if (key->data[i] == '*') {

                if (++n > 1) {

                    return NGX_DECLINED;

                }

            }

            if (key->data[i] == '.' && key->data[i + 1] == '.') {

                return NGX_DECLINED;

            }

        }

        if (key->len > 1 && key->data[0] == '.') {

            skip = 1;

            goto wildcard;

        }

        if (key->len > 2) {

            if (key->data[0] == '*' && key->data[1] == '.') {

                skip = 2;

                goto wildcard;

            }

            if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {

                skip = 0;

                last -= 2;

                goto wildcard;

            }

        }

        if (n) {

            return NGX_DECLINED;

        }

    }

    /* exact hash */

    k = 0;

    for (i = 0; i < last; i++) {

        if (!(flags & NGX_HASH_READONLY_KEY)) {

            key->data[i] = ngx_tolower(key->data[i]);

        }

        k = ngx_hash(k, key->data[i]);

    }

    k %= ha->hsize;

    /* check conflicts in exact hash */

    name = ha->keys_hash[k].elts;

    if (name) {

        for (i = 0; i < ha->keys_hash[k].nelts; i++) {

            if (last != name[i].len) {

                continue;

            }

            if (ngx_strncmp(key->data, name[i].data, last) == 0) {

                return NGX_BUSY;

            }

        }

    } else {

        if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,

                           sizeof(ngx_str_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    name = ngx_array_push(&ha->keys_hash[k]);

    if (name == NULL) {

        return NGX_ERROR;

    }

    *name = *key;

    hk = ngx_array_push(&ha->keys);

    if (hk == NULL) {

        return NGX_ERROR;

    }

    hk->key = *key;

    hk->key_hash = ngx_hash_key(key->data, last);

    hk->value = value;

    return NGX_OK;

wildcard:

    /* wildcard hash */

    k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);

    k %= ha->hsize;

    if (skip == 1) {

        /* check conflicts in exact hash for ".example.com" */

        name = ha->keys_hash[k].elts;

        if (name) {

            len = last - skip;

            for (i = 0; i < ha->keys_hash[k].nelts; i++) {

                if (len != name[i].len) {

                    continue;

                }

                if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {

                    return NGX_BUSY;

                }

            }

        } else {

            if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,

                               sizeof(ngx_str_t))

                != NGX_OK)

            {

                return NGX_ERROR;

            }

        }

        name = ngx_array_push(&ha->keys_hash[k]);

        if (name == NULL) {

            return NGX_ERROR;

        }

        name->len = last - 1;

        name->data = ngx_pnalloc(ha->temp_pool, name->len);

        if (name->data == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(name->data, &key->data[1], name->len);

    }

    if (skip) {

        /*

         * convert "*.example.com" to "com.example.\0"

         *      and ".example.com" to "com.example\0"

         */

        p = ngx_pnalloc(ha->temp_pool, last);

        if (p == NULL) {

            return NGX_ERROR;

        }

        len = 0;

        n = 0;

        for (i = last - 1; i; i--) {

            if (key->data[i] == '.') {

                ngx_memcpy(&p[n], &key->data[i + 1], len);

                n += len;

                p[n++] = '.';

                len = 0;

                continue;

            }

            len++;

        }

        if (len) {

            ngx_memcpy(&p[n], &key->data[1], len);

            n += len;

        }

        p[n] = '\0';

        hwc = &ha->dns_wc_head;

        keys = &ha->dns_wc_head_hash[k];

    } else {

        /* convert "www.example.*" to "www.example\0" */

        last++;

        p = ngx_pnalloc(ha->temp_pool, last);

        if (p == NULL) {

            return NGX_ERROR;

        }

        ngx_cpystrn(p, key->data, last);

        hwc = &ha->dns_wc_tail;

        keys = &ha->dns_wc_tail_hash[k];

    }

    /* check conflicts in wildcard hash */

    name = keys->elts;

    if (name) {

        len = last - skip;

        for (i = 0; i < keys->nelts; i++) {

            if (len != name[i].len) {

                continue;

            }

            if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {

                return NGX_BUSY;

            }

        }

    } else {

        if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    name = ngx_array_push(keys);

    if (name == NULL) {

        return NGX_ERROR;

    }

    name->len = last - skip;

    name->data = ngx_pnalloc(ha->temp_pool, name->len);

    if (name->data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(name->data, key->data + skip, name->len);

    /* add to wildcard hash */

    hk = ngx_array_push(hwc);

    if (hk == NULL) {

        return NGX_ERROR;

    }

    hk->key.len = last - 1;

    hk->key.data = p;

    hk->key_hash = 0;

    hk->value = value;

    return NGX_OK;

}

src/core/ngx_cycle.c - nginx-1.7.9

Global variables defined

dumb

ngx_cleaner_event

ngx_core_tls_key

ngx_cycle

ngx_old_cycles

ngx_quiet_mode

ngx_temp_pool

ngx_test_config

Functions defined

ngx_clean_old_cycles

ngx_create_pidfile

ngx_delete_pidfile

ngx_destroy_cycle_pools

ngx_init_cycle

ngx_init_zone_pool

ngx_reopen_files

ngx_shared_memory_add

ngx_signal_process

ngx_test_lockfile

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static void ngx_destroy_cycle_pools(ngx_conf_t *conf);

static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle,

    ngx_shm_zone_t *shm_zone);

static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);

static void ngx_clean_old_cycles(ngx_event_t *ev);

‌volatile ngx_cycle_t  *ngx_cycle;

‌ngx_array_t            ngx_old_cycles;

‌static ngx_pool_t     *ngx_temp_pool;

‌static ngx_event_t     ngx_cleaner_event;

‌ngx_uint_t             ngx_test_config;

‌ngx_uint_t             ngx_quiet_mode;

#if (NGX_THREADS)

‌ngx_tls_key_t          ngx_core_tls_key;

#endif

/* STUB NAME */

‌static ngx_connection_t  dumb;

/* STUB */

ngx_cycle_t *

‌ngx_init_cycle(ngx_cycle_t *old_cycle)

{

    void                *rv;

    char               **senv, **env;

    ngx_uint_t           i, n;

    ngx_log_t           *log;

    ngx_time_t          *tp;

    ngx_conf_t           conf;

    ngx_pool_t          *pool;

    ngx_cycle_t         *cycle, **old;

    ngx_shm_zone_t      *shm_zone, *oshm_zone;

    ngx_list_part_t     *part, *opart;

    ngx_open_file_t     *file;

    ngx_listening_t     *ls, *nls;

    ngx_core_conf_t     *ccf, *old_ccf;

    ngx_core_module_t   *module;

    char                 hostname[NGX_MAXHOSTNAMELEN];

    ngx_timezone_update();

    /* force localtime update with a new timezone */

    tp = ngx_timeofday();

    tp->sec = 0;

    ngx_time_update();

    log = old_cycle->log;

    pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);

    if (pool == NULL) {

        return NULL;

    }

    pool->log = log;

    cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));

    if (cycle == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    cycle->pool = pool;

    cycle->log = log;

    cycle->old_cycle = old_cycle;

    cycle->conf_prefix.len = old_cycle->conf_prefix.len;

    cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);

    if (cycle->conf_prefix.data == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    cycle->prefix.len = old_cycle->prefix.len;

    cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);

    if (cycle->prefix.data == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    cycle->conf_file.len = old_cycle->conf_file.len;

    cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);

    if (cycle->conf_file.data == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,

                old_cycle->conf_file.len + 1);

    cycle->conf_param.len = old_cycle->conf_param.len;

    cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);

    if (cycle->conf_param.data == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;

    cycle->paths.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));

    if (cycle->paths.elts == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    cycle->paths.nelts = 0;

    cycle->paths.size = sizeof(ngx_path_t *);

    cycle->paths.nalloc = n;

    cycle->paths.pool = pool;

    if (old_cycle->open_files.part.nelts) {

        n = old_cycle->open_files.part.nelts;

        for (part = old_cycle->open_files.part.next; part; part = part->next) {

            n += part->nelts;

        }

    } else {

        n = 20;

    }

    if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))

        != NGX_OK)

    {

        ngx_destroy_pool(pool);

        return NULL;

    }

    if (old_cycle->shared_memory.part.nelts) {

        n = old_cycle->shared_memory.part.nelts;

        for (part = old_cycle->shared_memory.part.next; part; part = part->next)

        {

            n += part->nelts;

        }

    } else {

        n = 1;

    }

    if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))

        != NGX_OK)

    {

        ngx_destroy_pool(pool);

        return NULL;

    }

    n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;

    cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));

    if (cycle->listening.elts == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    cycle->listening.nelts = 0;

    cycle->listening.size = sizeof(ngx_listening_t);

    cycle->listening.nalloc = n;

    cycle->listening.pool = pool;

    ngx_queue_init(&cycle->reusable_connections_queue);

    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));

    if (cycle->conf_ctx == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed");

        ngx_destroy_pool(pool);

        return NULL;

    }

    /* on Linux gethostname() silently truncates name that does not fit */

    hostname[NGX_MAXHOSTNAMELEN - 1] = '\0';

    cycle->hostname.len = ngx_strlen(hostname);

    cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);

    if (cycle->hostname.data == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_CORE_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->create_conf) {

            rv = module->create_conf(cycle);

            if (rv == NULL) {

                ngx_destroy_pool(pool);

                return NULL;

            }

            cycle->conf_ctx[ngx_modules[i]->index] = rv;

        }

    }

    senv = environ;

    ngx_memzero(&conf, sizeof(ngx_conf_t));

    /* STUB: init array ? */

    conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));

    if (conf.args == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);

    if (conf.temp_pool == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    conf.ctx = cycle->conf_ctx;

    conf.cycle = cycle;

    conf.pool = pool;

    conf.log = log;

    conf.module_type = NGX_CORE_MODULE;

    conf.cmd_type = NGX_MAIN_CONF;

#if 0

    log->log_level = NGX_LOG_DEBUG_ALL;

#endif

    if (ngx_conf_param(&conf) != NGX_CONF_OK) {

        environ = senv;

        ngx_destroy_cycle_pools(&conf);

        return NULL;

    }

    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {

        environ = senv;

        ngx_destroy_cycle_pools(&conf);

        return NULL;

    }

    if (ngx_test_config && !ngx_quiet_mode) {

        ngx_log_stderr(0, "the configuration file %s syntax is ok",

                       cycle->conf_file.data);

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_CORE_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->init_conf) {

            if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])

                == NGX_CONF_ERROR)

            {

                environ = senv;

                ngx_destroy_cycle_pools(&conf);

                return NULL;

            }

        }

    }

    if (ngx_process == NGX_PROCESS_SIGNALLER) {

        return cycle;

    }

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ngx_test_config) {

        if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {

            goto failed;

        }

    } else if (!ngx_is_init_cycle(old_cycle)) {

        /*

         * we do not create the pid file in the first ngx_init_cycle() call

         * because we need to write the demonized process pid

         */

        old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,

                                                   ngx_core_module);

        if (ccf->pid.len != old_ccf->pid.len

            || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)

        {

            /* new pid file name */

            if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {

                goto failed;

            }

            ngx_delete_pidfile(old_cycle);

        }

    }

    if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {

        goto failed;

    }

    if (ngx_create_paths(cycle, ccf->user) != NGX_OK) {

        goto failed;

    }

    if (ngx_log_open_default(cycle) != NGX_OK) {

        goto failed;

    }

    /* open the new files */

    part = &cycle->open_files.part;

    file = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            file = part->elts;

            i = 0;

        }

        if (file[i].name.len == 0) {

            continue;

        }

        file[i].fd = ngx_open_file(file[i].name.data,

                                   NGX_FILE_APPEND,

                                   NGX_FILE_CREATE_OR_OPEN,

                                   NGX_FILE_DEFAULT_ACCESS);

        ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,

                       "log: %p %d \"%s\"",

                       &file[i], file[i].fd, file[i].name.data);

        if (file[i].fd == NGX_INVALID_FILE) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                          ngx_open_file_n " \"%s\" failed",

                          file[i].name.data);

            goto failed;

        }

#if !(NGX_WIN32)

        if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                          "fcntl(FD_CLOEXEC) \"%s\" failed",

                          file[i].name.data);

            goto failed;

        }

#endif

    }

    cycle->log = &cycle->new_log;

    pool->log = &cycle->new_log;

    /* create shared memory */

    part = &cycle->shared_memory.part;

    shm_zone = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            shm_zone = part->elts;

            i = 0;

        }

        if (shm_zone[i].shm.size == 0) {

            ngx_log_error(NGX_LOG_EMERG, log, 0,

                          "zero size shared memory zone \"%V\"",

                          &shm_zone[i].shm.name);

            goto failed;

        }

        shm_zone[i].shm.log = cycle->log;

        opart = &old_cycle->shared_memory.part;

        oshm_zone = opart->elts;

        for (n = 0; /* void */ ; n++) {

            if (n >= opart->nelts) {

                if (opart->next == NULL) {

                    break;

                }

                opart = opart->next;

                oshm_zone = opart->elts;

                n = 0;

            }

            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {

                continue;

            }

            if (ngx_strncmp(shm_zone[i].shm.name.data,

                            oshm_zone[n].shm.name.data,

                            shm_zone[i].shm.name.len)

                != 0)

            {

                continue;

            }

            if (shm_zone[i].tag == oshm_zone[n].tag

                && shm_zone[i].shm.size == oshm_zone[n].shm.size)

            {

                shm_zone[i].shm.addr = oshm_zone[n].shm.addr;

                if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)

                    != NGX_OK)

                {

                    goto failed;

                }

                goto shm_zone_found;

            }

            ngx_shm_free(&oshm_zone[n].shm);

            break;

        }

        if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {

            goto failed;

        }

        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {

            goto failed;

        }

        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {

            goto failed;

        }

    shm_zone_found:

        continue;

    }

    /* handle the listening sockets */

    if (old_cycle->listening.nelts) {

        ls = old_cycle->listening.elts;

        for (i = 0; i < old_cycle->listening.nelts; i++) {

            ls[i].remain = 0;

        }

        nls = cycle->listening.elts;

        for (n = 0; n < cycle->listening.nelts; n++) {

            for (i = 0; i < old_cycle->listening.nelts; i++) {

                if (ls[i].ignore) {

                    continue;

                }

                if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen,

                                     ls[i].sockaddr, ls[i].socklen, 1)

                    == NGX_OK)

                {

                    nls[n].fd = ls[i].fd;

                    nls[n].previous = &ls[i];

                    ls[i].remain = 1;

                    if (ls[i].backlog != nls[n].backlog) {

                        nls[n].listen = 1;

                    }

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

                    /*

                     * FreeBSD, except the most recent versions,

                     * could not remove accept filter

                     */

                    nls[n].deferred_accept = ls[i].deferred_accept;

                    if (ls[i].accept_filter && nls[n].accept_filter) {

                        if (ngx_strcmp(ls[i].accept_filter,

                                       nls[n].accept_filter)

                            != 0)

                        {

                            nls[n].delete_deferred = 1;

                            nls[n].add_deferred = 1;

                        }

                    } else if (ls[i].accept_filter) {

                        nls[n].delete_deferred = 1;

                    } else if (nls[n].accept_filter) {

                        nls[n].add_deferred = 1;

                    }

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

                    if (ls[i].deferred_accept && !nls[n].deferred_accept) {

                        nls[n].delete_deferred = 1;

                    } else if (ls[i].deferred_accept != nls[n].deferred_accept)

                    {

                        nls[n].add_deferred = 1;

                    }

#endif

                    break;

                }

            }

            if (nls[n].fd == (ngx_socket_t) -1) {

                nls[n].open = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

                if (nls[n].accept_filter) {

                    nls[n].add_deferred = 1;

                }

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

                if (nls[n].deferred_accept) {

                    nls[n].add_deferred = 1;

                }

#endif

            }

        }

    } else {

        ls = cycle->listening.elts;

        for (i = 0; i < cycle->listening.nelts; i++) {

            ls[i].open = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

            if (ls[i].accept_filter) {

                ls[i].add_deferred = 1;

            }

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

            if (ls[i].deferred_accept) {

                ls[i].add_deferred = 1;

            }

#endif

        }

    }

    if (ngx_open_listening_sockets(cycle) != NGX_OK) {

        goto failed;

    }

    if (!ngx_test_config) {

        ngx_configure_listening_sockets(cycle);

    }

    /* commit the new cycle configuration */

    if (!ngx_use_stderr) {

        (void) ngx_log_redirect_stderr(cycle);

    }

    pool->log = cycle->log;

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->init_module) {

            if (ngx_modules[i]->init_module(cycle) != NGX_OK) {

                /* fatal */

                exit(1);

            }

        }

    }

    /* close and delete stuff that lefts from an old cycle */

    /* free the unnecessary shared memory */

    opart = &old_cycle->shared_memory.part;

    oshm_zone = opart->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= opart->nelts) {

            if (opart->next == NULL) {

                goto old_shm_zone_done;

            }

            opart = opart->next;

            oshm_zone = opart->elts;

            i = 0;

        }

        part = &cycle->shared_memory.part;

        shm_zone = part->elts;

        for (n = 0; /* void */ ; n++) {

            if (n >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                shm_zone = part->elts;

                n = 0;

            }

            if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len

                && ngx_strncmp(oshm_zone[i].shm.name.data,

                               shm_zone[n].shm.name.data,

                               oshm_zone[i].shm.name.len)

                == 0)

            {

                goto live_shm_zone;

            }

        }

        ngx_shm_free(&oshm_zone[i].shm);

    live_shm_zone:

        continue;

    }

old_shm_zone_done:

    /* close the unnecessary listening sockets */

    ls = old_cycle->listening.elts;

    for (i = 0; i < old_cycle->listening.nelts; i++) {

        if (ls[i].remain || ls[i].fd == (ngx_socket_t) -1) {

            continue;

        }

        if (ngx_close_socket(ls[i].fd) == -1) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                          ngx_close_socket_n " listening socket on %V failed",

                          &ls[i].addr_text);

        }

#if (NGX_HAVE_UNIX_DOMAIN)

        if (ls[i].sockaddr->sa_family == AF_UNIX) {

            u_char  *name;

            name = ls[i].addr_text.data + sizeof("unix:") - 1;

            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,

                          "deleting socket %s", name);

            if (ngx_delete_file(name) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,

                              ngx_delete_file_n " %s failed", name);

            }

        }

#endif

    }

    /* close the unnecessary open files */

    part = &old_cycle->open_files.part;

    file = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            file = part->elts;

            i = 0;

        }

        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {

            continue;

        }

        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed",

                          file[i].name.data);

        }

    }

    ngx_destroy_pool(conf.temp_pool);

    if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {

        /*

         * perl_destruct() frees environ, if it is not the same as it was at

         * perl_construct() time, therefore we save the previous cycle

         * environment before ngx_conf_parse() where it will be changed.

         */

        env = environ;

        environ = senv;

        ngx_destroy_pool(old_cycle->pool);

        cycle->old_cycle = NULL;

        environ = env;

        return cycle;

    }

    if (ngx_temp_pool == NULL) {

        ngx_temp_pool = ngx_create_pool(128, cycle->log);

        if (ngx_temp_pool == NULL) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                          "could not create ngx_temp_pool");

            exit(1);

        }

        n = 10;

        ngx_old_cycles.elts = ngx_pcalloc(ngx_temp_pool,

                                          n * sizeof(ngx_cycle_t *));

        if (ngx_old_cycles.elts == NULL) {

            exit(1);

        }

        ngx_old_cycles.nelts = 0;

        ngx_old_cycles.size = sizeof(ngx_cycle_t *);

        ngx_old_cycles.nalloc = n;

        ngx_old_cycles.pool = ngx_temp_pool;

        ngx_cleaner_event.handler = ngx_clean_old_cycles;

        ngx_cleaner_event.log = cycle->log;

        ngx_cleaner_event.data = &dumb;

        dumb.fd = (ngx_socket_t) -1;

    }

    ngx_temp_pool->log = cycle->log;

    old = ngx_array_push(&ngx_old_cycles);

    if (old == NULL) {

        exit(1);

    }

    *old = old_cycle;

    if (!ngx_cleaner_event.timer_set) {

        ngx_add_timer(&ngx_cleaner_event, 30000);

        ngx_cleaner_event.timer_set = 1;

    }

    return cycle;

failed:

    if (!ngx_is_init_cycle(old_cycle)) {

        old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,

                                                   ngx_core_module);

        if (old_ccf->environment) {

            environ = old_ccf->environment;

        }

    }

    /* rollback the new cycle configuration */

    part = &cycle->open_files.part;

    file = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            file = part->elts;

            i = 0;

        }

        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {

            continue;

        }

        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed",

                          file[i].name.data);

        }

    }

    if (ngx_test_config) {

        ngx_destroy_cycle_pools(&conf);

        return NULL;

    }

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        if (ls[i].fd == (ngx_socket_t) -1 || !ls[i].open) {

            continue;

        }

        if (ngx_close_socket(ls[i].fd) == -1) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,

                          ngx_close_socket_n " %V failed",

                          &ls[i].addr_text);

        }

    }

    ngx_destroy_cycle_pools(&conf);

    return NULL;

}

static void

‌ngx_destroy_cycle_pools(ngx_conf_t *conf)

{

    ngx_destroy_pool(conf->temp_pool);

    ngx_destroy_pool(conf->pool);

}

static ngx_int_t

‌ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)

{

    u_char           *file;

    ngx_slab_pool_t  *sp;

    sp = (ngx_slab_pool_t *) zn->shm.addr;

    if (zn->shm.exists) {

        if (sp == sp->addr) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                      "shared zone \"%V\" has no equal addresses: %p vs %p",

                      &zn->shm.name, sp->addr, sp);

        return NGX_ERROR;

    }

    sp->end = zn->shm.addr + zn->shm.size;

    sp->min_shift = 3;

    sp->addr = zn->shm.addr;

#if (NGX_HAVE_ATOMIC_OPS)

    file = NULL;

#else

    file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len);

    if (file == NULL) {

        return NGX_ERROR;

    }

    (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name);

#endif

    if (ngx_shmtx_create(&sp->mutex, &sp->lock, file) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_slab_init(sp);

    return NGX_OK;

}

ngx_int_t

‌ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)

{

    size_t      len;

    ngx_uint_t  create;

    ngx_file_t  file;

    u_char      pid[NGX_INT64_LEN + 2];

    if (ngx_process > NGX_PROCESS_MASTER) {

        return NGX_OK;

    }

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = *name;

    file.log = log;

    create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;

    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR,

                            create, NGX_FILE_DEFAULT_ACCESS);

    if (file.fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", file.name.data);

        return NGX_ERROR;

    }

    if (!ngx_test_config) {

        len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid;

        if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) {

            return NGX_ERROR;

        }

    }

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", file.name.data);

    }

    return NGX_OK;

}

void

‌ngx_delete_pidfile(ngx_cycle_t *cycle)

{

    u_char           *name;

    ngx_core_conf_t  *ccf;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data;

    if (ngx_delete_file(name) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      ngx_delete_file_n " \"%s\" failed", name);

    }

}

ngx_int_t

‌ngx_signal_process(ngx_cycle_t *cycle, char *sig)

{

    ssize_t           n;

    ngx_int_t         pid;

    ngx_file_t        file;

    ngx_core_conf_t  *ccf;

    u_char            buf[NGX_INT64_LEN + 2];

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = ccf->pid;

    file.log = cycle->log;

    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,

                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);

    if (file.fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", file.name.data);

        return 1;

    }

    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", file.name.data);

    }

    if (n == NGX_ERROR) {

        return 1;

    }

    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }

    pid = ngx_atoi(buf, ++n);

    if (pid == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,

                      "invalid PID number \"%*s\" in \"%s\"",

                      n, buf, file.name.data);

        return 1;

    }

    return ngx_os_signal_process(cycle, sig, pid);

}

static ngx_int_t

‌ngx_test_lockfile(u_char *file, ngx_log_t *log)

{

#if !(NGX_HAVE_ATOMIC_OPS)

    ngx_fd_t  fd;

    fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,

                       NGX_FILE_DEFAULT_ACCESS);

    if (fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", file);

        return NGX_ERROR;

    }

    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", file);

    }

    if (ngx_delete_file(file) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_delete_file_n " \"%s\" failed", file);

    }

#endif

    return NGX_OK;

}

void

‌ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)

{

    ngx_fd_t          fd;

    ngx_uint_t        i;

    ngx_list_part_t  *part;

    ngx_open_file_t  *file;

    part = &cycle->open_files.part;

    file = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            file = part->elts;

            i = 0;

        }

        if (file[i].name.len == 0) {

            continue;

        }

        if (file[i].flush) {

            file[i].flush(&file[i], cycle->log);

        }

        fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND,

                           NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "reopen file \"%s\", old:%d new:%d",

                       file[i].name.data, file[i].fd, fd);

        if (fd == NGX_INVALID_FILE) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          ngx_open_file_n " \"%s\" failed", file[i].name.data);

            continue;

        }

#if !(NGX_WIN32)

        if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {

            ngx_file_info_t  fi;

            if (ngx_file_info((const char *) file[i].name.data, &fi)

                == NGX_FILE_ERROR)

            {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                              ngx_file_info_n " \"%s\" failed",

                              file[i].name.data);

                if (ngx_close_file(fd) == NGX_FILE_ERROR) {

                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                  ngx_close_file_n " \"%s\" failed",

                                  file[i].name.data);

                }

                continue;

            }

            if (fi.st_uid != user) {

                if (chown((const char *) file[i].name.data, user, -1) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                  "chown(\"%s\", %d) failed",

                                  file[i].name.data, user);

                    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                      ngx_close_file_n " \"%s\" failed",

                                      file[i].name.data);

                    }

                    continue;

                }

            }

            if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) {

                fi.st_mode |= (S_IRUSR|S_IWUSR);

                if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) {

                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                  "chmod() \"%s\" failed", file[i].name.data);

                    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                                      ngx_close_file_n " \"%s\" failed",

                                      file[i].name.data);

                    }

                    continue;

                }

            }

        }

        if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "fcntl(FD_CLOEXEC) \"%s\" failed",

                          file[i].name.data);

            if (ngx_close_file(fd) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                              ngx_close_file_n " \"%s\" failed",

                              file[i].name.data);

            }

            continue;

        }

#endif

        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed",

                          file[i].name.data);

        }

        file[i].fd = fd;

    }

    (void) ngx_log_redirect_stderr(cycle);

}

ngx_shm_zone_t *

‌ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)

{

    ngx_uint_t        i;

    ngx_shm_zone_t   *shm_zone;

    ngx_list_part_t  *part;

    part = &cf->cycle->shared_memory.part;

    shm_zone = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            shm_zone = part->elts;

            i = 0;

        }

        if (name->len != shm_zone[i].shm.name.len) {

            continue;

        }

        if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)

            != 0)

        {

            continue;

        }

        if (tag != shm_zone[i].tag) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                            "the shared memory zone \"%V\" is "

                            "already declared for a different use",

                            &shm_zone[i].shm.name);

            return NULL;

        }

        if (size && size != shm_zone[i].shm.size) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                            "the size %uz of shared memory zone \"%V\" "

                            "conflicts with already declared size %uz",

                            size, &shm_zone[i].shm.name, shm_zone[i].shm.size);

            return NULL;

        }

        return &shm_zone[i];

    }

    shm_zone = ngx_list_push(&cf->cycle->shared_memory);

    if (shm_zone == NULL) {

        return NULL;

    }

    shm_zone->data = NULL;

    shm_zone->shm.log = cf->cycle->log;

    shm_zone->shm.size = size;

    shm_zone->shm.name = *name;

    shm_zone->shm.exists = 0;

    shm_zone->init = NULL;

    shm_zone->tag = tag;

    return shm_zone;

}

static void

‌ngx_clean_old_cycles(ngx_event_t *ev)

{

    ngx_uint_t     i, n, found, live;

    ngx_log_t     *log;

    ngx_cycle_t  **cycle;

    log = ngx_cycle->log;

    ngx_temp_pool->log = log;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycles");

    live = 0;

    cycle = ngx_old_cycles.elts;

    for (i = 0; i < ngx_old_cycles.nelts; i++) {

        if (cycle[i] == NULL) {

            continue;

        }

        found = 0;

        for (n = 0; n < cycle[i]->connection_n; n++) {

            if (cycle[i]->connections[n].fd != (ngx_socket_t) -1) {

                found = 1;

                ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "live fd:%d", n);

                break;

            }

        }

        if (found) {

            live = 1;

            continue;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycle: %d", i);

        ngx_destroy_pool(cycle[i]->pool);

        cycle[i] = NULL;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "old cycles status: %d", live);

    if (live) {

        ngx_add_timer(ev, 30000);

    } else {

        ngx_destroy_pool(ngx_temp_pool);

        ngx_temp_pool = NULL;

        ngx_old_cycles.nelts = 0;

    }

}

src/core/ngx_cycle.h - nginx-1.7.9

Data types defined

ngx_core_conf_t

ngx_core_tls_t

ngx_cycle_s

ngx_shm_zone_init_pt

ngx_shm_zone_s

ngx_shm_zone_t

Macros defined

NGX_CYCLE_POOL_SIZE

NGX_DEBUG_POINTS_ABORT

NGX_DEBUG_POINTS_STOP

_NGX_CYCLE_H_INCLUDED_

ngx_is_init_cycle

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CYCLE_H_INCLUDED_

‌#define _NGX_CYCLE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#ifndef NGX_CYCLE_POOL_SIZE

‌#define NGX_CYCLE_POOL_SIZE     NGX_DEFAULT_POOL_SIZE

#endif

‌#define NGX_DEBUG_POINTS_STOP   1

‌#define NGX_DEBUG_POINTS_ABORT  2

‌typedef struct ngx_shm_zone_s  ngx_shm_zone_t;

‌typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);

‌struct ngx_shm_zone_s {

    void                     *data;

    ngx_shm_t                 shm;

    ngx_shm_zone_init_pt      init;

    void                     *tag;

};

‌struct ngx_cycle_s {

    void                  ****conf_ctx;

    ngx_pool_t               *pool;

    ngx_log_t                *log;

    ngx_log_t                 new_log;

    ngx_uint_t                log_use_stderr;  /* unsigned  log_use_stderr:1; */

    ngx_connection_t        **files;

    ngx_connection_t         *free_connections;

    ngx_uint_t                free_connection_n;

    ngx_queue_t               reusable_connections_queue;

    ngx_array_t               listening;

    ngx_array_t               paths;

    ngx_list_t                open_files;

    ngx_list_t                shared_memory;

    ngx_uint_t                connection_n;

    ngx_uint_t                files_n;

    ngx_connection_t         *connections;

    ngx_event_t              *read_events;

    ngx_event_t              *write_events;

    ngx_cycle_t              *old_cycle;

    ngx_str_t                 conf_file;

    ngx_str_t                 conf_param;

    ngx_str_t                 conf_prefix;

    ngx_str_t                 prefix;

    ngx_str_t                 lock_file;

    ngx_str_t                 hostname;

};

typedef struct {

     ngx_flag_t               daemon;

     ngx_flag_t               master;

     ngx_msec_t               timer_resolution;

     ngx_int_t                worker_processes;

     ngx_int_t                debug_points;

     ngx_int_t                rlimit_nofile;

     ngx_int_t                rlimit_sigpending;

     off_t                    rlimit_core;

     int                      priority;

     ngx_uint_t               cpu_affinity_n;

     uint64_t                *cpu_affinity;

     char                    *username;

     ngx_uid_t                user;

     ngx_gid_t                group;

     ngx_str_t                working_directory;

     ngx_str_t                lock_file;

     ngx_str_t                pid;

     ngx_str_t                oldpid;

     ngx_array_t              env;

     char                   **environment;

#if (NGX_THREADS)

     ngx_int_t                worker_threads;

     size_t                   thread_stack_size;

#endif

‌} ngx_core_conf_t;

typedef struct {

     ngx_pool_t              *pool;   /* pcre's malloc() pool */

‌} ngx_core_tls_t;

‌#define ngx_is_init_cycle(cycle)  (cycle->conf_ctx == NULL)

ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);

ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log);

void ngx_delete_pidfile(ngx_cycle_t *cycle);

ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig);

void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);

char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last);

ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);

uint64_t ngx_get_cpu_affinity(ngx_uint_t n);

ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,

    size_t size, void *tag);

extern volatile ngx_cycle_t  *ngx_cycle;

extern ngx_array_t            ngx_old_cycles;

extern ngx_module_t           ngx_core_module;

extern ngx_uint_t             ngx_test_config;

extern ngx_uint_t             ngx_quiet_mode;

#if (NGX_THREADS)

extern ngx_tls_key_t          ngx_core_tls_key;

#endif

#endif /* _NGX_CYCLE_H_INCLUDED_ */

src/os/unix/ngx_shmem.h - nginx-1.7.9

Data types defined

ngx_shm_t

Macros defined

_NGX_SHMEM_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SHMEM_H_INCLUDED_

‌#define _NGX_SHMEM_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    u_char      *addr;

    size_t       size;

    ngx_str_t    name;

    ngx_log_t   *log;

    ngx_uint_t   exists;   /* unsigned  exists:1;  */

‌} ngx_shm_t;

ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);

void ngx_shm_free(ngx_shm_t *shm);

#endif /* _NGX_SHMEM_H_INCLUDED_ */

src/os/unix/ngx_shmem.c - nginx-1.7.9

Functions defined

ngx_shm_alloc

ngx_shm_alloc

ngx_shm_alloc

ngx_shm_free

ngx_shm_free

ngx_shm_free

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_MAP_ANON)

ngx_int_t

‌ngx_shm_alloc(ngx_shm_t *shm)

{

    shm->addr = (u_char *) mmap(NULL, shm->size,

                                PROT_READ|PROT_WRITE,

                                MAP_ANON|MAP_SHARED, -1, 0);

    if (shm->addr == MAP_FAILED) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);

        return NGX_ERROR;

    }

    return NGX_OK;

}

void

‌ngx_shm_free(ngx_shm_t *shm)

{

    if (munmap((void *) shm->addr, shm->size) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "munmap(%p, %uz) failed", shm->addr, shm->size);

    }

}

#elif (NGX_HAVE_MAP_DEVZERO)

ngx_int_t

‌ngx_shm_alloc(ngx_shm_t *shm)

{

    ngx_fd_t  fd;

    fd = open("/dev/zero", O_RDWR);

    if (fd == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "open(\"/dev/zero\") failed");

        return NGX_ERROR;

    }

    shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,

                                MAP_SHARED, fd, 0);

    if (shm->addr == MAP_FAILED) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);

    }

    if (close(fd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "close(\"/dev/zero\") failed");

    }

    return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;

}

void

‌ngx_shm_free(ngx_shm_t *shm)

{

    if (munmap((void *) shm->addr, shm->size) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "munmap(%p, %uz) failed", shm->addr, shm->size);

    }

}

#elif (NGX_HAVE_SYSVSHM)

#include <sys/ipc.h>

#include <sys/shm.h>

ngx_int_t

‌ngx_shm_alloc(ngx_shm_t *shm)

{

    int  id;

    id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));

    if (id == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmget(%uz) failed", shm->size);

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);

    shm->addr = shmat(id, NULL, 0);

    if (shm->addr == (void *) -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");

    }

    if (shmctl(id, IPC_RMID, NULL) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmctl(IPC_RMID) failed");

    }

    return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;

}

void

‌ngx_shm_free(ngx_shm_t *shm)

{

    if (shmdt(shm->addr) == -1) {

        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,

                      "shmdt(%p) failed", shm->addr);

    }

}

#endif

src/os/unix/ngx_user.h - nginx-1.7.9

Data types defined

ngx_gid_t

ngx_uid_t

Macros defined

_NGX_USER_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_USER_H_INCLUDED_

‌#define _NGX_USER_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef uid_t  ngx_uid_t;

‌typedef gid_t  ngx_gid_t;

ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

#endif /* _NGX_USER_H_INCLUDED_ */

src/os/unix/ngx_user.c - nginx-1.7.9

Functions defined

ngx_libc_crypt

ngx_libc_crypt

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* Solaris has thread-safe crypt()

* Linux has crypt_r(); "struct crypt_data" is more than 128K

* FreeBSD needs the mutex to protect crypt()

*

* TODO:

*     ngx_crypt_init() to init mutex

*/

#if (NGX_CRYPT)

#if (NGX_HAVE_GNU_CRYPT_R)

ngx_int_t

‌ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    char               *value;

    size_t              len;

    struct crypt_data   cd;

    cd.initialized = 0;

    /* work around the glibc bug */

    cd.current_salt[0] = ~salt[0];

    value = crypt_r((char *) key, (char *) salt, &cd);

    if (value) {

        len = ngx_strlen(value) + 1;

        *encrypted = ngx_pnalloc(pool, len);

        if (*encrypted == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(*encrypted, value, len);

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_CRIT, pool->log, ngx_errno, "crypt_r() failed");

    return NGX_ERROR;

}

#else

ngx_int_t

‌ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    char       *value;

    size_t      len;

    ngx_err_t   err;

#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)

    /* crypt() is a time consuming function, so we only try to lock */

    if (ngx_mutex_trylock(ngx_crypt_mutex) != NGX_OK) {

        return NGX_AGAIN;

    }

#endif

    value = crypt((char *) key, (char *) salt);

    if (value) {

        len = ngx_strlen(value) + 1;

        *encrypted = ngx_pnalloc(pool, len);

        if (*encrypted == NULL) {

#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)

            ngx_mutex_unlock(ngx_crypt_mutex);

#endif

            return NGX_ERROR;

        }

        ngx_memcpy(*encrypted, value, len);

#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)

        ngx_mutex_unlock(ngx_crypt_mutex);

#endif

        return NGX_OK;

    }

    err = ngx_errno;

#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)

    ngx_mutex_unlock(ngx_crypt_mutex);

#endif

    ngx_log_error(NGX_LOG_CRIT, pool->log, err, "crypt() failed");

    return NGX_ERROR;

}

#endif

#endif /* NGX_CRYPT */

src/os/unix/ngx_pthread_thread.c - nginx-1.7.9

Global variables defined

max_threads

nthreads

thr_attr

Functions defined

ngx_cond_destroy

ngx_cond_init

ngx_cond_signal

ngx_cond_wait

ngx_create_thread

ngx_init_threads

ngx_mutex_destroy

ngx_mutex_init

ngx_mutex_lock

ngx_mutex_trylock

ngx_mutex_unlock

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌static ngx_uint_t   nthreads;

‌static ngx_uint_t   max_threads;

‌static pthread_attr_t  thr_attr;

ngx_err_t

‌ngx_create_thread(ngx_tid_t *tid, ngx_thread_value_t (*func)(void *arg),

    void *arg, ngx_log_t *log)

{

    int  err;

    if (nthreads >= max_threads) {

        ngx_log_error(NGX_LOG_CRIT, log, 0,

                      "no more than %ui threads can be created", max_threads);

        return NGX_ERROR;

    }

    err = pthread_create(tid, &thr_attr, func, arg);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_create() failed");

        return err;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,

                   "thread is created: " NGX_TID_T_FMT, *tid);

    nthreads++;

    return err;

}

ngx_int_t

‌ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle)

{

    int  err;

    max_threads = n;

    err = pthread_attr_init(&thr_attr);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                      "pthread_attr_init() failed");

        return NGX_ERROR;

    }

    err = pthread_attr_setstacksize(&thr_attr, size);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                      "pthread_attr_setstacksize() failed");

        return NGX_ERROR;

    }

    ngx_threaded = 1;

    return NGX_OK;

}

ngx_mutex_t *

‌ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags)

{

    int           err;

    ngx_mutex_t  *m;

    m = ngx_alloc(sizeof(ngx_mutex_t), log);

    if (m == NULL) {

        return NULL;

    }

    m->log = log;

    err = pthread_mutex_init(&m->mutex, NULL);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, m->log, err,

                      "pthread_mutex_init() failed");

        return NULL;

    }

    return m;

}

void

‌ngx_mutex_destroy(ngx_mutex_t *m)

{

    int  err;

    err = pthread_mutex_destroy(&m->mutex);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, m->log, err,

                      "pthread_mutex_destroy(%p) failed", m);

    }

    ngx_free(m);

}

void

‌ngx_mutex_lock(ngx_mutex_t *m)

{

    int  err;

    if (!ngx_threaded) {

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "lock mutex %p", m);

    err = pthread_mutex_lock(&m->mutex);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, m->log, err,

                      "pthread_mutex_lock(%p) failed", m);

        ngx_abort();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);

    return;

}

ngx_int_t

‌ngx_mutex_trylock(ngx_mutex_t *m)

{

    int  err;

    if (!ngx_threaded) {

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "try lock mutex %p", m);

    err = pthread_mutex_trylock(&m->mutex);

    if (err == NGX_EBUSY) {

        return NGX_AGAIN;

    }

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, m->log, err,

                      "pthread_mutex_trylock(%p) failed", m);

        ngx_abort();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);

    return NGX_OK;

}

void

‌ngx_mutex_unlock(ngx_mutex_t *m)

{

    int  err;

    if (!ngx_threaded) {

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "unlock mutex %p", m);

    err = pthread_mutex_unlock(&m->mutex);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, m->log, err,

                      "pthread_mutex_unlock(%p) failed", m);

        ngx_abort();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is unlocked", m);

    return;

}

ngx_cond_t *

‌ngx_cond_init(ngx_log_t *log)

{

    int          err;

    ngx_cond_t  *cv;

    cv = ngx_alloc(sizeof(ngx_cond_t), log);

    if (cv == NULL) {

        return NULL;

    }

    cv->log = log;

    err = pthread_cond_init(&cv->cond, NULL);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cv->log, err,

                      "pthread_cond_init() failed");

        return NULL;

    }

    return cv;

}

void

‌ngx_cond_destroy(ngx_cond_t *cv)

{

    int  err;

    err = pthread_cond_destroy(&cv->cond);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cv->log, err,

                      "pthread_cond_destroy(%p) failed", cv);

    }

    ngx_free(cv);

}

ngx_int_t

‌ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m)

{

    int  err;

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p wait", cv);

    err = pthread_cond_wait(&cv->cond, &m->mutex);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cv->log, err,

                      "pthread_cond_wait(%p) failed", cv);

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is waked up", cv);

    ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);

    return NGX_OK;

}

ngx_int_t

‌ngx_cond_signal(ngx_cond_t *cv)

{

    int  err;

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p to signal", cv);

    err = pthread_cond_signal(&cv->cond);

    if (err != 0) {

        ngx_log_error(NGX_LOG_ALERT, cv->log, err,

                      "pthread_cond_signal(%p) failed", cv);

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is signaled", cv);

    return NGX_OK;

}

src/core/ngx_times.h - nginx-1.7.9

Data types defined

ngx_time_t

Macros defined

_NGX_TIMES_H_INCLUDED_

ngx_next_time_n

ngx_time

ngx_timeofday

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_TIMES_H_INCLUDED_

‌#define _NGX_TIMES_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    time_t      sec;

    ngx_uint_t  msec;

    ngx_int_t   gmtoff;

‌} ngx_time_t;

void ngx_time_init(void);

void ngx_time_update(void);

void ngx_time_sigsafe_update(void);

u_char *ngx_http_time(u_char *buf, time_t t);

u_char *ngx_http_cookie_time(u_char *buf, time_t t);

void ngx_gmtime(time_t t, ngx_tm_t *tp);

time_t ngx_next_time(time_t when);

‌#define ngx_next_time_n      "mktime()"

extern volatile ngx_time_t  *ngx_cached_time;

‌#define ngx_time()           ngx_cached_time->sec

‌#define ngx_timeofday()      (ngx_time_t *) ngx_cached_time

extern volatile ngx_str_t    ngx_cached_err_log_time;

extern volatile ngx_str_t    ngx_cached_http_time;

extern volatile ngx_str_t    ngx_cached_http_log_time;

extern volatile ngx_str_t    ngx_cached_http_log_iso8601;

extern volatile ngx_str_t    ngx_cached_syslog_time;

/*

* milliseconds elapsed since epoch and truncated to ngx_msec_t,

* used in event timers

*/

extern volatile ngx_msec_t  ngx_current_msec;

#endif /* _NGX_TIMES_H_INCLUDED_ */

src/os/unix/ngx_time.c - nginx-1.7.9

Functions defined

ngx_libc_gmtime

ngx_libc_localtime

ngx_localtime

ngx_timezone_update

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* FreeBSD does not test /etc/localtime change, however, we can workaround it

* by calling tzset() with TZ and then without TZ to update timezone.

* The trick should work since FreeBSD 2.1.0.

*

* Linux does not test /etc/localtime change in localtime(),

* but may stat("/etc/localtime") several times in every strftime(),

* therefore we use it to update timezone.

*

* Solaris does not test /etc/TIMEZONE change too and no workaround available.

*/

void

‌ngx_timezone_update(void)

{

#if (NGX_FREEBSD)

    if (getenv("TZ")) {

        return;

    }

    putenv("TZ=UTC");

    tzset();

    unsetenv("TZ");

    tzset();

#elif (NGX_LINUX)

    time_t      s;

    struct tm  *t;

    char        buf[4];

    s = time(0);

    t = localtime(&s);

    strftime(buf, 4, "%H", t);

#endif

}

void

‌ngx_localtime(time_t s, ngx_tm_t *tm)

{

#if (NGX_HAVE_LOCALTIME_R)

    (void) localtime_r(&s, tm);

#else

    ngx_tm_t  *t;

    t = localtime(&s);

    *tm = *t;

#endif

    tm->ngx_tm_mon++;

    tm->ngx_tm_year += 1900;

}

void

‌ngx_libc_localtime(time_t s, struct tm *tm)

{

#if (NGX_HAVE_LOCALTIME_R)

    (void) localtime_r(&s, tm);

#else

    struct tm  *t;

    t = localtime(&s);

    *tm = *t;

#endif

}

void

‌ngx_libc_gmtime(time_t s, struct tm *tm)

{

#if (NGX_HAVE_LOCALTIME_R)

    (void) gmtime_r(&s, tm);

#else

    struct tm  *t;

    t = gmtime(&s);

    *tm = *t;

#endif

}

src/core/ngx_md5.c - nginx-1.7.9

Functions defined

ngx_md5_body

ngx_md5_final

ngx_md5_init

ngx_md5_update

Macros defined

F

G

GET

GET

H

I

SET

SET

STEP

Source code

/*

* An internal implementation, based on Alexander Peslyak's

* public domain implementation:

* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5

* It is not expected to be optimal and is used only

* if no MD5 implementation was found in system.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_md5.h>

#if !(NGX_HAVE_MD5)

static const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data,

    size_t size);

void

‌ngx_md5_init(ngx_md5_t *ctx)

{

    ctx->a = 0x67452301;

    ctx->b = 0xefcdab89;

    ctx->c = 0x98badcfe;

    ctx->d = 0x10325476;

    ctx->bytes = 0;

}

void

‌ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size)

{

    size_t  used, free;

    used = (size_t) (ctx->bytes & 0x3f);

    ctx->bytes += size;

    if (used) {

        free = 64 - used;

        if (size < free) {

            ngx_memcpy(&ctx->buffer[used], data, size);

            return;

        }

        ngx_memcpy(&ctx->buffer[used], data, free);

        data = (u_char *) data + free;

        size -= free;

        (void) ngx_md5_body(ctx, ctx->buffer, 64);

    }

    if (size >= 64) {

        data = ngx_md5_body(ctx, data, size & ~(size_t) 0x3f);

        size &= 0x3f;

    }

    ngx_memcpy(ctx->buffer, data, size);

}

void

‌ngx_md5_final(u_char result[16], ngx_md5_t *ctx)

{

    size_t  used, free;

    used = (size_t) (ctx->bytes & 0x3f);

    ctx->buffer[used++] = 0x80;

    free = 64 - used;

    if (free < 8) {

        ngx_memzero(&ctx->buffer[used], free);

        (void) ngx_md5_body(ctx, ctx->buffer, 64);

        used = 0;

        free = 64;

    }

    ngx_memzero(&ctx->buffer[used], free - 8);

    ctx->bytes <<= 3;

    ctx->buffer[56] = (u_char) ctx->bytes;

    ctx->buffer[57] = (u_char) (ctx->bytes >> 8);

    ctx->buffer[58] = (u_char) (ctx->bytes >> 16);

    ctx->buffer[59] = (u_char) (ctx->bytes >> 24);

    ctx->buffer[60] = (u_char) (ctx->bytes >> 32);

    ctx->buffer[61] = (u_char) (ctx->bytes >> 40);

    ctx->buffer[62] = (u_char) (ctx->bytes >> 48);

    ctx->buffer[63] = (u_char) (ctx->bytes >> 56);

    (void) ngx_md5_body(ctx, ctx->buffer, 64);

    result[0] = (u_char) ctx->a;

    result[1] = (u_char) (ctx->a >> 8);

    result[2] = (u_char) (ctx->a >> 16);

    result[3] = (u_char) (ctx->a >> 24);

    result[4] = (u_char) ctx->b;

    result[5] = (u_char) (ctx->b >> 8);

    result[6] = (u_char) (ctx->b >> 16);

    result[7] = (u_char) (ctx->b >> 24);

    result[8] = (u_char) ctx->c;

    result[9] = (u_char) (ctx->c >> 8);

    result[10] = (u_char) (ctx->c >> 16);

    result[11] = (u_char) (ctx->c >> 24);

    result[12] = (u_char) ctx->d;

    result[13] = (u_char) (ctx->d >> 8);

    result[14] = (u_char) (ctx->d >> 16);

    result[15] = (u_char) (ctx->d >> 24);

    ngx_memzero(ctx, sizeof(*ctx));

}

/*

* The basic MD5 functions.

*

* F and G are optimized compared to their RFC 1321 definitions for

* architectures that lack an AND-NOT instruction, just like in

* Colin Plumb's implementation.

*/

‌#define F(x, y, z)  ((z) ^ ((x) & ((y) ^ (z))))

‌#define G(x, y, z)  ((y) ^ ((z) & ((x) ^ (y))))

‌#define H(x, y, z)  ((x) ^ (y) ^ (z))

‌#define I(x, y, z)  ((y) ^ ((x) | ~(z)))

/*

* The MD5 transformation for all four rounds.

*/

‌#define STEP(f, a, b, c, d, x, t, s)                                          \

    (a) += f((b), (c), (d)) + (x) + (t);                                      \

    (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));                \

    (a) += (b)

/*

* SET() reads 4 input bytes in little-endian byte order and stores them

* in a properly aligned word in host byte order.

*

* The check for little-endian architectures that tolerate unaligned

* memory accesses is just an optimization.  Nothing will break if it

* does not work.

*/

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

‌#define SET(n)      (*(uint32_t *) &p[n * 4])

‌#define GET(n)      (*(uint32_t *) &p[n * 4])

#else

‌#define SET(n)                                                                \

    (block[n] =                                                               \

    (uint32_t) p[n * 4] |                                                     \

    ((uint32_t) p[n * 4 + 1] << 8) |                                          \

    ((uint32_t) p[n * 4 + 2] << 16) |                                         \

    ((uint32_t) p[n * 4 + 3] << 24))

‌#define GET(n)      block[n]

#endif

/*

* This processes one or more 64-byte data blocks, but does not update

* the bit counters.  There are no alignment requirements.

*/

static const u_char *

‌ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size)

{

    uint32_t       a, b, c, d;

    uint32_t       saved_a, saved_b, saved_c, saved_d;

    const u_char  *p;

#if !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

    uint32_t       block[16];

#endif

    p = data;

    a = ctx->a;

    b = ctx->b;

    c = ctx->c;

    d = ctx->d;

    do {

        saved_a = a;

        saved_b = b;

        saved_c = c;

        saved_d = d;

        /* Round 1 */

        STEP(F, a, b, c, d, SET(0),  0xd76aa478, 7);

        STEP(F, d, a, b, c, SET(1),  0xe8c7b756, 12);

        STEP(F, c, d, a, b, SET(2),  0x242070db, 17);

        STEP(F, b, c, d, a, SET(3),  0xc1bdceee, 22);

        STEP(F, a, b, c, d, SET(4),  0xf57c0faf, 7);

        STEP(F, d, a, b, c, SET(5),  0x4787c62a, 12);

        STEP(F, c, d, a, b, SET(6),  0xa8304613, 17);

        STEP(F, b, c, d, a, SET(7),  0xfd469501, 22);

        STEP(F, a, b, c, d, SET(8),  0x698098d8, 7);

        STEP(F, d, a, b, c, SET(9),  0x8b44f7af, 12);

        STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17);

        STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22);

        STEP(F, a, b, c, d, SET(12), 0x6b901122, 7);

        STEP(F, d, a, b, c, SET(13), 0xfd987193, 12);

        STEP(F, c, d, a, b, SET(14), 0xa679438e, 17);

        STEP(F, b, c, d, a, SET(15), 0x49b40821, 22);

        /* Round 2 */

        STEP(G, a, b, c, d, GET(1),  0xf61e2562, 5);

        STEP(G, d, a, b, c, GET(6),  0xc040b340, 9);

        STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14);

        STEP(G, b, c, d, a, GET(0),  0xe9b6c7aa, 20);

        STEP(G, a, b, c, d, GET(5),  0xd62f105d, 5);

        STEP(G, d, a, b, c, GET(10), 0x02441453, 9);

        STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14);

        STEP(G, b, c, d, a, GET(4),  0xe7d3fbc8, 20);

        STEP(G, a, b, c, d, GET(9),  0x21e1cde6, 5);

        STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9);

        STEP(G, c, d, a, b, GET(3),  0xf4d50d87, 14);

        STEP(G, b, c, d, a, GET(8),  0x455a14ed, 20);

        STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5);

        STEP(G, d, a, b, c, GET(2),  0xfcefa3f8, 9);

        STEP(G, c, d, a, b, GET(7),  0x676f02d9, 14);

        STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20);

        /* Round 3 */

        STEP(H, a, b, c, d, GET(5),  0xfffa3942, 4);

        STEP(H, d, a, b, c, GET(8),  0x8771f681, 11);

        STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16);

        STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23);

        STEP(H, a, b, c, d, GET(1),  0xa4beea44, 4);

        STEP(H, d, a, b, c, GET(4),  0x4bdecfa9, 11);

        STEP(H, c, d, a, b, GET(7),  0xf6bb4b60, 16);

        STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23);

        STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4);

        STEP(H, d, a, b, c, GET(0),  0xeaa127fa, 11);

        STEP(H, c, d, a, b, GET(3),  0xd4ef3085, 16);

        STEP(H, b, c, d, a, GET(6),  0x04881d05, 23);

        STEP(H, a, b, c, d, GET(9),  0xd9d4d039, 4);

        STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11);

        STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16);

        STEP(H, b, c, d, a, GET(2),  0xc4ac5665, 23);

        /* Round 4 */

        STEP(I, a, b, c, d, GET(0),  0xf4292244, 6);

        STEP(I, d, a, b, c, GET(7),  0x432aff97, 10);

        STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15);

        STEP(I, b, c, d, a, GET(5),  0xfc93a039, 21);

        STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6);

        STEP(I, d, a, b, c, GET(3),  0x8f0ccc92, 10);

        STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15);

        STEP(I, b, c, d, a, GET(1),  0x85845dd1, 21);

        STEP(I, a, b, c, d, GET(8),  0x6fa87e4f, 6);

        STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10);

        STEP(I, c, d, a, b, GET(6),  0xa3014314, 15);

        STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21);

        STEP(I, a, b, c, d, GET(4),  0xf7537e82, 6);

        STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10);

        STEP(I, c, d, a, b, GET(2),  0x2ad7d2bb, 15);

        STEP(I, b, c, d, a, GET(9),  0xeb86d391, 21);

        a += saved_a;

        b += saved_b;

        c += saved_c;

        d += saved_d;

        p += 64;

    } while (size -= 64);

    ctx->a = a;

    ctx->b = b;

    ctx->c = c;

    ctx->d = d;

    return p;

}

#endif

src/core/ngx_md5.h - nginx-1.7.9

Data types defined

ngx_md5_t

ngx_md5_t

Macros defined

_NGX_MD5_H_INCLUDED_

ngx_md5_final

ngx_md5_final

ngx_md5_init

ngx_md5_init

ngx_md5_update

ngx_md5_update

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MD5_H_INCLUDED_

‌#define _NGX_MD5_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_MD5)

#if (NGX_HAVE_OPENSSL_MD5_H)

#include <openssl/md5.h>

#else

#include <md5.h>

#endif

‌typedef MD5_CTX  ngx_md5_t;

#if (NGX_OPENSSL_MD5)

‌#define ngx_md5_init    MD5_Init

‌#define ngx_md5_update  MD5_Update

‌#define ngx_md5_final   MD5_Final

#else

‌#define ngx_md5_init    MD5Init

‌#define ngx_md5_update  MD5Update

‌#define ngx_md5_final   MD5Final

#endif

#else /* !NGX_HAVE_MD5 */

typedef struct {

    uint64_t  bytes;

    uint32_t  a, b, c, d;

    u_char    buffer[64];

‌} ngx_md5_t;

void ngx_md5_init(ngx_md5_t *ctx);

void ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size);

void ngx_md5_final(u_char result[16], ngx_md5_t *ctx);

#endif

#endif /* _NGX_MD5_H_INCLUDED_ */

src/core/ngx_file.h - nginx-1.7.9

Data types defined

ngx_copy_file_t

ngx_ext_rename_file_t

ngx_file_s

ngx_path_init_t

ngx_path_loader_pt

ngx_path_manager_pt

ngx_path_t

ngx_temp_file_t

ngx_tree_ctx_s

ngx_tree_ctx_t

ngx_tree_handler_pt

ngx_tree_init_handler_pt

Macros defined

NGX_MAX_PATH_LEVEL

_NGX_FILE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_FILE_H_INCLUDED_

‌#define _NGX_FILE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌struct ngx_file_s {

    ngx_fd_t                   fd;

    ngx_str_t                  name;

    ngx_file_info_t            info;

    off_t                      offset;

    off_t                      sys_offset;

    ngx_log_t                 *log;

#if (NGX_HAVE_FILE_AIO)

    ngx_event_aio_t           *aio;

#endif

    unsigned                   valid_info:1;

    unsigned                   directio:1;

};

‌#define NGX_MAX_PATH_LEVEL  3

‌typedef time_t (*ngx_path_manager_pt) (void *data);

‌typedef void (*ngx_path_loader_pt) (void *data);

typedef struct {

    ngx_str_t                  name;

    size_t                     len;

    size_t                     level[3];

    ngx_path_manager_pt        manager;

    ngx_path_loader_pt         loader;

    void                      *data;

    u_char                    *conf_file;

    ngx_uint_t                 line;

‌} ngx_path_t;

typedef struct {

    ngx_str_t                  name;

    size_t                     level[3];

‌} ngx_path_init_t;

typedef struct {

    ngx_file_t                 file;

    off_t                      offset;

    ngx_path_t                *path;

    ngx_pool_t                *pool;

    char                      *warn;

    ngx_uint_t                 access;

    unsigned                   log_level:8;

    unsigned                   persistent:1;

    unsigned                   clean:1;

‌} ngx_temp_file_t;

typedef struct {

    ngx_uint_t                 access;

    ngx_uint_t                 path_access;

    time_t                     time;

    ngx_fd_t                   fd;

    unsigned                   create_path:1;

    unsigned                   delete_file:1;

    ngx_log_t                 *log;

‌} ngx_ext_rename_file_t;

typedef struct {

    off_t                      size;

    size_t                     buf_size;

    ngx_uint_t                 access;

    time_t                     time;

    ngx_log_t                 *log;

‌} ngx_copy_file_t;

‌typedef struct ngx_tree_ctx_s  ngx_tree_ctx_t;

‌typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);

‌typedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name);

‌struct ngx_tree_ctx_s {

    off_t                      size;

    off_t                      fs_size;

    ngx_uint_t                 access;

    time_t                     mtime;

    ngx_tree_init_handler_pt   init_handler;

    ngx_tree_handler_pt        file_handler;

    ngx_tree_handler_pt        pre_tree_handler;

    ngx_tree_handler_pt        post_tree_handler;

    ngx_tree_handler_pt        spec_handler;

    void                      *data;

    size_t                     alloc;

    ngx_log_t                 *log;

};

ngx_int_t ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix,

    ngx_str_t *name);

ssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain);

ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,

    ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean,

    ngx_uint_t access);

void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len);

ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path);

ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);

ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);

ngx_int_t ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user);

ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,

    ngx_ext_rename_file_t *ext);

ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);

ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);

ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision);

char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path,

    ngx_path_t *prev, ngx_path_init_t *init);

char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

extern ngx_atomic_t      *ngx_temp_number;

extern ngx_atomic_int_t   ngx_random_number;

#endif /* _NGX_FILE_H_INCLUDED_ */

src/core/ngx_file.c - nginx-1.7.9

Global variables defined

ngx_random_number

ngx_temp_number

temp_number

Functions defined

ngx_add_path

ngx_conf_merge_path_value

ngx_conf_set_access_slot

ngx_conf_set_path_slot

ngx_copy_file

ngx_create_full_path

ngx_create_hashed_filename

ngx_create_path

ngx_create_paths

ngx_create_temp_file

ngx_ext_rename_file

ngx_get_full_name

ngx_next_temp_number

ngx_test_full_name

ngx_walk_tree

ngx_write_chain_to_temp_file

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static ngx_int_t ngx_test_full_name(ngx_str_t *name);

‌static ngx_atomic_t   temp_number = 0;

‌ngx_atomic_t         *ngx_temp_number = &temp_number;

‌ngx_atomic_int_t      ngx_random_number = 123456;

ngx_int_t

‌ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, ngx_str_t *name)

{

    size_t      len;

    u_char     *p, *n;

    ngx_int_t   rc;

    rc = ngx_test_full_name(name);

    if (rc == NGX_OK) {

        return rc;

    }

    len = prefix->len;

#if (NGX_WIN32)

    if (rc == 2) {

        len = rc;

    }

#endif

    n = ngx_pnalloc(pool, len + name->len + 1);

    if (n == NULL) {

        return NGX_ERROR;

    }

    p = ngx_cpymem(n, prefix->data, len);

    ngx_cpystrn(p, name->data, name->len + 1);

    name->len += len;

    name->data = n;

    return NGX_OK;

}

static ngx_int_t

‌ngx_test_full_name(ngx_str_t *name)

{

#if (NGX_WIN32)

    u_char  c0, c1;

    c0 = name->data[0];

    if (name->len < 2) {

        if (c0 == '/') {

            return 2;

        }

        return NGX_DECLINED;

    }

    c1 = name->data[1];

    if (c1 == ':') {

        c0 |= 0x20;

        if ((c0 >= 'a' && c0 <= 'z')) {

            return NGX_OK;

        }

        return NGX_DECLINED;

    }

    if (c1 == '/') {

        return NGX_OK;

    }

    if (c0 == '/') {

        return 2;

    }

    return NGX_DECLINED;

#else

    if (name->data[0] == '/') {

        return NGX_OK;

    }

    return NGX_DECLINED;

#endif

}

ssize_t

‌ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)

{

    ngx_int_t  rc;

    if (tf->file.fd == NGX_INVALID_FILE) {

        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,

                                  tf->persistent, tf->clean, tf->access);

        if (rc == NGX_ERROR || rc == NGX_AGAIN) {

            return rc;

        }

        if (tf->log_level) {

            ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",

                          tf->warn, &tf->file.name);

        }

    }

    return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);

}

ngx_int_t

‌ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,

    ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)

{

    uint32_t                  n;

    ngx_err_t                 err;

    ngx_pool_cleanup_t       *cln;

    ngx_pool_cleanup_file_t  *clnf;

    file->name.len = path->name.len + 1 + path->len + 10;

    file->name.data = ngx_pnalloc(pool, file->name.len + 1);

    if (file->name.data == NULL) {

        return NGX_ERROR;

    }

#if 0

    for (i = 0; i < file->name.len; i++) {

         file->name.data[i] = 'X';

    }

#endif

    ngx_memcpy(file->name.data, path->name.data, path->name.len);

    n = (uint32_t) ngx_next_temp_number(0);

    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));

    if (cln == NULL) {

        return NGX_ERROR;

    }

    for ( ;; ) {

        (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,

                           "%010uD%Z", n);

        ngx_create_hashed_filename(path, file->name.data, file->name.len);

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,

                       "hashed path: %s", file->name.data);

        file->fd = ngx_open_tempfile(file->name.data, persistent, access);

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,

                       "temp fd:%d", file->fd);

        if (file->fd != NGX_INVALID_FILE) {

            cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;

            clnf = cln->data;

            clnf->fd = file->fd;

            clnf->name = file->name.data;

            clnf->log = pool->log;

            return NGX_OK;

        }

        err = ngx_errno;

        if (err == NGX_EEXIST) {

            n = (uint32_t) ngx_next_temp_number(1);

            continue;

        }

        if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {

            ngx_log_error(NGX_LOG_CRIT, file->log, err,

                          ngx_open_tempfile_n " \"%s\" failed",

                          file->name.data);

            return NGX_ERROR;

        }

        if (ngx_create_path(file, path) == NGX_ERROR) {

            return NGX_ERROR;

        }

    }

}

void

‌ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)

{

    size_t      i, level;

    ngx_uint_t  n;

    i = path->name.len + 1;

    file[path->name.len + path->len]  = '/';

    for (n = 0; n < 3; n++) {

        level = path->level[n];

        if (level == 0) {

            break;

        }

        len -= level;

        file[i - 1] = '/';

        ngx_memcpy(&file[i], &file[len], level);

        i += level + 1;

    }

}

ngx_int_t

‌ngx_create_path(ngx_file_t *file, ngx_path_t *path)

{

    size_t      pos;

    ngx_err_t   err;

    ngx_uint_t  i;

    pos = path->name.len;

    for (i = 0; i < 3; i++) {

        if (path->level[i] == 0) {

            break;

        }

        pos += path->level[i] + 1;

        file->name.data[pos] = '\0';

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,

                       "temp file: \"%s\"", file->name.data);

        if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {

            err = ngx_errno;

            if (err != NGX_EEXIST) {

                ngx_log_error(NGX_LOG_CRIT, file->log, err,

                              ngx_create_dir_n " \"%s\" failed",

                              file->name.data);

                return NGX_ERROR;

            }

        }

        file->name.data[pos] = '/';

    }

    return NGX_OK;

}

ngx_err_t

‌ngx_create_full_path(u_char *dir, ngx_uint_t access)

{

    u_char     *p, ch;

    ngx_err_t   err;

    err = 0;

#if (NGX_WIN32)

    p = dir + 3;

#else

    p = dir + 1;

#endif

    for ( /* void */ ; *p; p++) {

        ch = *p;

        if (ch != '/') {

            continue;

        }

        *p = '\0';

        if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {

            err = ngx_errno;

            switch (err) {

            case NGX_EEXIST:

                err = 0;

            case NGX_EACCES:

                break;

            default:

                return err;

            }

        }

        *p = '/';

    }

    return err;

}

ngx_atomic_uint_t

‌ngx_next_temp_number(ngx_uint_t collision)

{

    ngx_atomic_uint_t  n, add;

    add = collision ? ngx_random_number : 1;

    n = ngx_atomic_fetch_add(ngx_temp_number, add);

    return n + add;

}

char *

‌ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ssize_t      level;

    ngx_str_t   *value;

    ngx_uint_t   i, n;

    ngx_path_t  *path, **slot;

    slot = (ngx_path_t **) (p + cmd->offset);

    if (*slot) {

        return "is duplicate";

    }

    path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));

    if (path == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    path->name = value[1];

    if (path->name.data[path->name.len - 1] == '/') {

        path->name.len--;

    }

    if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {

        return NULL;

    }

    path->conf_file = cf->conf_file->file.name.data;

    path->line = cf->conf_file->line;

    for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {

        level = ngx_atoi(value[n].data, value[n].len);

        if (level == NGX_ERROR || level == 0) {

            return "invalid value";

        }

        path->level[i] = level;

        path->len += level + 1;

    }

    while (i < 3) {

        path->level[i++] = 0;

    }

    *slot = path;

    if (ngx_add_path(cf, slot) == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,

    ngx_path_init_t *init)

{

    if (*path) {

        return NGX_CONF_OK;

    }

    if (prev) {

        *path = prev;

        return NGX_CONF_OK;

    }

    *path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));

    if (*path == NULL) {

        return NGX_CONF_ERROR;

    }

    (*path)->name = init->name;

    if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    (*path)->level[0] = init->level[0];

    (*path)->level[1] = init->level[1];

    (*path)->level[2] = init->level[2];

    (*path)->len = init->level[0] + (init->level[0] ? 1 : 0)

                   + init->level[1] + (init->level[1] ? 1 : 0)

                   + init->level[2] + (init->level[2] ? 1 : 0);

    if (ngx_add_path(cf, path) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *confp = conf;

    u_char      *p;

    ngx_str_t   *value;

    ngx_uint_t   i, right, shift, *access;

    access = (ngx_uint_t *) (confp + cmd->offset);

    if (*access != NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *access = 0600;

    for (i = 1; i < cf->args->nelts; i++) {

        p = value[i].data;

        if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {

            shift = 6;

            p += sizeof("user:") - 1;

        } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {

            shift = 3;

            p += sizeof("group:") - 1;

        } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {

            shift = 0;

            p += sizeof("all:") - 1;

        } else {

            goto invalid;

        }

        if (ngx_strcmp(p, "rw") == 0) {

            right = 6;

        } else if (ngx_strcmp(p, "r") == 0) {

            right = 4;

        } else {

            goto invalid;

        }

        *access |= right << shift;

    }

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]);

    return NGX_CONF_ERROR;

}

ngx_int_t

‌ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)

{

    ngx_uint_t   i, n;

    ngx_path_t  *path, **p;

    path = *slot;

    p = cf->cycle->paths.elts;

    for (i = 0; i < cf->cycle->paths.nelts; i++) {

        if (p[i]->name.len == path->name.len

            && ngx_strcmp(p[i]->name.data, path->name.data) == 0)

        {

            if (p[i]->data != path->data) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "the same path name \"%V\" "

                                   "used in %s:%ui and",

                                   &p[i]->name, p[i]->conf_file, p[i]->line);

                return NGX_ERROR;

            }

            for (n = 0; n < 3; n++) {

                if (p[i]->level[n] != path->level[n]) {

                    if (path->conf_file == NULL) {

                        if (p[i]->conf_file == NULL) {

                            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                                      "the default path name \"%V\" has "

                                      "the same name as another default path, "

                                      "but the different levels, you need to "

                                      "redefine one of them in http section",

                                      &p[i]->name);

                            return NGX_ERROR;

                        }

                        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                                      "the path name \"%V\" in %s:%ui has "

                                      "the same name as default path, but "

                                      "the different levels, you need to "

                                      "define default path in http section",

                                      &p[i]->name, p[i]->conf_file, p[i]->line);

                        return NGX_ERROR;

                    }

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                      "the same path name \"%V\" in %s:%ui "

                                      "has the different levels than",

                                      &p[i]->name, p[i]->conf_file, p[i]->line);

                    return NGX_ERROR;

                }

                if (p[i]->level[n] == 0) {

                    break;

                }

            }

            *slot = p[i];

            return NGX_OK;

        }

    }

    p = ngx_array_push(&cf->cycle->paths);

    if (p == NULL) {

        return NGX_ERROR;

    }

    *p = path;

    return NGX_OK;

}

ngx_int_t

‌ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user)

{

    ngx_err_t         err;

    ngx_uint_t        i;

    ngx_path_t      **path;

    path = cycle->paths.elts;

    for (i = 0; i < cycle->paths.nelts; i++) {

        if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {

            err = ngx_errno;

            if (err != NGX_EEXIST) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, err,

                              ngx_create_dir_n " \"%s\" failed",

                              path[i]->name.data);

                return NGX_ERROR;

            }

        }

        if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {

            continue;

        }

#if !(NGX_WIN32)

        {

        ngx_file_info_t   fi;

        if (ngx_file_info((const char *) path[i]->name.data, &fi)

            == NGX_FILE_ERROR)

        {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          ngx_file_info_n " \"%s\" failed", path[i]->name.data);

            return NGX_ERROR;

        }

        if (fi.st_uid != user) {

            if (chown((const char *) path[i]->name.data, user, -1) == -1) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                              "chown(\"%s\", %d) failed",

                              path[i]->name.data, user);

                return NGX_ERROR;

            }

        }

        if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))

                                                  != (S_IRUSR|S_IWUSR|S_IXUSR))

        {

            fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);

            if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {

                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                              "chmod() \"%s\" failed", path[i]->name.data);

                return NGX_ERROR;

            }

        }

        }

#endif

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)

{

    u_char           *name;

    ngx_err_t         err;

    ngx_copy_file_t   cf;

#if !(NGX_WIN32)

    if (ext->access) {

        if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                          ngx_change_file_access_n " \"%s\" failed", src->data);

            err = 0;

            goto failed;

        }

    }

#endif

    if (ext->time != -1) {

        if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {

            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                          ngx_set_file_time_n " \"%s\" failed", src->data);

            err = 0;

            goto failed;

        }

    }

    if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {

        return NGX_OK;

    }

    err = ngx_errno;

    if (err == NGX_ENOPATH) {

        if (!ext->create_path) {

            goto failed;

        }

        err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));

        if (err) {

            ngx_log_error(NGX_LOG_CRIT, ext->log, err,

                          ngx_create_dir_n " \"%s\" failed", to->data);

            err = 0;

            goto failed;

        }

        if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {

            return NGX_OK;

        }

        err = ngx_errno;

    }

#if (NGX_WIN32)

    if (err == NGX_EEXIST) {

        err = ngx_win32_rename_file(src, to, ext->log);

        if (err == 0) {

            return NGX_OK;

        }

    }

#endif

    if (err == NGX_EXDEV) {

        cf.size = -1;

        cf.buf_size = 0;

        cf.access = ext->access;

        cf.time = ext->time;

        cf.log = ext->log;

        name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);

        if (name == NULL) {

            return NGX_ERROR;

        }

        (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data,

                           (uint32_t) ngx_next_temp_number(0));

        if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {

            if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) {

                ngx_free(name);

                if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {

                    ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                                  ngx_delete_file_n " \"%s\" failed",

                                  src->data);

                    return NGX_ERROR;

                }

                return NGX_OK;

            }

            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                          ngx_rename_file_n " \"%s\" to \"%s\" failed",

                          name, to->data);

            if (ngx_delete_file(name) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                              ngx_delete_file_n " \"%s\" failed", name);

            }

        }

        ngx_free(name);

        err = 0;

    }

failed:

    if (ext->delete_file) {

        if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,

                          ngx_delete_file_n " \"%s\" failed", src->data);

        }

    }

    if (err) {

        ngx_log_error(NGX_LOG_CRIT, ext->log, err,

                      ngx_rename_file_n " \"%s\" to \"%s\" failed",

                      src->data, to->data);

    }

    return NGX_ERROR;

}

ngx_int_t

‌ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)

{

    char             *buf;

    off_t             size;

    size_t            len;

    ssize_t           n;

    ngx_fd_t          fd, nfd;

    ngx_int_t         rc;

    ngx_file_info_t   fi;

    rc = NGX_ERROR;

    buf = NULL;

    nfd = NGX_INVALID_FILE;

    fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

    if (fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", from);

        goto failed;

    }

    if (cf->size != -1) {

        size = cf->size;

    } else {

        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_fd_info_n " \"%s\" failed", from);

            goto failed;

        }

        size = ngx_file_size(&fi);

    }

    len = cf->buf_size ? cf->buf_size : 65536;

    if ((off_t) len > size) {

        len = (size_t) size;

    }

    buf = ngx_alloc(len, cf->log);

    if (buf == NULL) {

        goto failed;

    }

    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,

                        cf->access);

    if (nfd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", to);

        goto failed;

    }

    while (size > 0) {

        if ((off_t) len > size) {

            len = (size_t) size;

        }

        n = ngx_read_fd(fd, buf, len);

        if (n == -1) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_read_fd_n " \"%s\" failed", from);

            goto failed;

        }

        if ((size_t) n != len) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,

                          ngx_read_fd_n " has read only %z of %uz from %s",

                          n, size, from);

            goto failed;

        }

        n = ngx_write_fd(nfd, buf, len);

        if (n == -1) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_write_fd_n " \"%s\" failed", to);

            goto failed;

        }

        if ((size_t) n != len) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,

                          ngx_write_fd_n " has written only %z of %uz to %s",

                          n, size, to);

            goto failed;

        }

        size -= n;

    }

    if (cf->time != -1) {

        if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_set_file_time_n " \"%s\" failed", to);

            goto failed;

        }

    }

    rc = NGX_OK;

failed:

    if (nfd != NGX_INVALID_FILE) {

        if (ngx_close_file(nfd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed", to);

        }

    }

    if (fd != NGX_INVALID_FILE) {

        if (ngx_close_file(fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed", from);

        }

    }

    if (buf) {

        ngx_free(buf);

    }

    return rc;

}

/*

* ctx->init_handler() - see ctx->alloc

* ctx->file_handler() - file handler

* ctx->pre_tree_handler() - handler is called before entering directory

* ctx->post_tree_handler() - handler is called after leaving directory

* ctx->spec_handler() - special (socket, FIFO, etc.) file handler

*

* ctx->data - some data structure, it may be the same on all levels, or

*     reallocated if ctx->alloc is nonzero

*

* ctx->alloc - a size of data structure that is allocated at every level

*     and is initialized by ctx->init_handler()

*

* ctx->log - a log

*

* on fatal (memory) error handler must return NGX_ABORT to stop walking tree

*/

ngx_int_t

‌ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)

{

    void       *data, *prev;

    u_char     *p, *name;

    size_t      len;

    ngx_int_t   rc;

    ngx_err_t   err;

    ngx_str_t   file, buf;

    ngx_dir_t   dir;

    ngx_str_null(&buf);

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                   "walk tree \"%V\"", tree);

    if (ngx_open_dir(tree, &dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                      ngx_open_dir_n " \"%s\" failed", tree->data);

        return NGX_ERROR;

    }

    prev = ctx->data;

    if (ctx->alloc) {

        data = ngx_alloc(ctx->alloc, ctx->log);

        if (data == NULL) {

            goto failed;

        }

        if (ctx->init_handler(data, prev) == NGX_ABORT) {

            goto failed;

        }

        ctx->data = data;

    } else {

        data = NULL;

    }

    for ( ;; ) {

        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {

            err = ngx_errno;

            if (err == NGX_ENOMOREFILES) {

                rc = NGX_OK;

            } else {

                ngx_log_error(NGX_LOG_CRIT, ctx->log, err,

                              ngx_read_dir_n " \"%s\" failed", tree->data);

                rc = NGX_ERROR;

            }

            goto done;

        }

        len = ngx_de_namelen(&dir);

        name = ngx_de_name(&dir);

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                      "tree name %uz:\"%s\"", len, name);

        if (len == 1 && name[0] == '.') {

            continue;

        }

        if (len == 2 && name[0] == '.' && name[1] == '.') {

            continue;

        }

        file.len = tree->len + 1 + len;

        if (file.len + NGX_DIR_MASK_LEN > buf.len) {

            if (buf.len) {

                ngx_free(buf.data);

            }

            buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;

            buf.data = ngx_alloc(buf.len + 1, ctx->log);

            if (buf.data == NULL) {

                goto failed;

            }

        }

        p = ngx_cpymem(buf.data, tree->data, tree->len);

        *p++ = '/';

        ngx_memcpy(p, name, len + 1);

        file.data = buf.data;

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                       "tree path \"%s\"", file.data);

        if (!dir.valid_info) {

            if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                              ngx_de_info_n " \"%s\" failed", file.data);

                continue;

            }

        }

        if (ngx_de_is_file(&dir)) {

            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                           "tree file \"%s\"", file.data);

            ctx->size = ngx_de_size(&dir);

            ctx->fs_size = ngx_de_fs_size(&dir);

            ctx->access = ngx_de_access(&dir);

            ctx->mtime = ngx_de_mtime(&dir);

            if (ctx->file_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

            }

        } else if (ngx_de_is_dir(&dir)) {

            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                           "tree enter dir \"%s\"", file.data);

            ctx->access = ngx_de_access(&dir);

            ctx->mtime = ngx_de_mtime(&dir);

            if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

            }

            if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {

                goto failed;

            }

            ctx->access = ngx_de_access(&dir);

            ctx->mtime = ngx_de_mtime(&dir);

            if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

            }

        } else {

            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                           "tree special \"%s\"", file.data);

            if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

            }

        }

    }

failed:

    rc = NGX_ABORT;

done:

    if (buf.len) {

        ngx_free(buf.data);

    }

    if (data) {

        ngx_free(data);

        ctx->data = prev;

    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                      ngx_close_dir_n " \"%s\" failed", tree->data);

    }

    return rc;

}

src/core/ngx_conf_file.c - nginx-1.7.9

Global variables defined

argument_number

ngx_conf_commands

ngx_conf_module

Functions defined

ngx_conf_check_num_bounds

ngx_conf_deprecated

ngx_conf_flush_files

ngx_conf_full_name

ngx_conf_handler

ngx_conf_include

ngx_conf_log_error

ngx_conf_open_file

ngx_conf_param

ngx_conf_parse

ngx_conf_read_token

ngx_conf_set_bitmask_slot

ngx_conf_set_bufs_slot

ngx_conf_set_enum_slot

ngx_conf_set_flag_slot

ngx_conf_set_keyval_slot

ngx_conf_set_msec_slot

ngx_conf_set_num_slot

ngx_conf_set_off_slot

ngx_conf_set_sec_slot

ngx_conf_set_size_slot

ngx_conf_set_str_array_slot

ngx_conf_set_str_slot

Macros defined

NGX_CONF_BUFFER

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_CONF_BUFFER  4096

static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);

static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);

static void ngx_conf_flush_files(ngx_cycle_t *cycle);

‌static ngx_command_t  ngx_conf_commands[] = {

    { ngx_string("include"),

      NGX_ANY_CONF|NGX_CONF_TAKE1,

      ngx_conf_include,

      0,

      0,

      NULL },

      ngx_null_command

};

‌ngx_module_t  ngx_conf_module = {

    NGX_MODULE_V1,

    NULL,                                  /* module context */

    ngx_conf_commands,                     /* module directives */

    NGX_CONF_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    ngx_conf_flush_files,                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

/* The eight fixed arguments */

‌static ngx_uint_t argument_number[] = {

    NGX_CONF_NOARGS,

    NGX_CONF_TAKE1,

    NGX_CONF_TAKE2,

    NGX_CONF_TAKE3,

    NGX_CONF_TAKE4,

    NGX_CONF_TAKE5,

    NGX_CONF_TAKE6,

    NGX_CONF_TAKE7

};

char *

‌ngx_conf_param(ngx_conf_t *cf)

{

    char             *rv;

    ngx_str_t        *param;

    ngx_buf_t         b;

    ngx_conf_file_t   conf_file;

    param = &cf->cycle->conf_param;

    if (param->len == 0) {

        return NGX_CONF_OK;

    }

    ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));

    ngx_memzero(&b, sizeof(ngx_buf_t));

    b.start = param->data;

    b.pos = param->data;

    b.last = param->data + param->len;

    b.end = b.last;

    b.temporary = 1;

    conf_file.file.fd = NGX_INVALID_FILE;

    conf_file.file.name.data = NULL;

    conf_file.line = 0;

    cf->conf_file = &conf_file;

    cf->conf_file->buffer = &b;

    rv = ngx_conf_parse(cf, NULL);

    cf->conf_file = NULL;

    return rv;

}

char *

‌ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)

{

    char             *rv;

    ngx_fd_t          fd;

    ngx_int_t         rc;

    ngx_buf_t         buf;

    ngx_conf_file_t  *prev, conf_file;

    enum {

        parse_file = 0,

        parse_block,

        parse_param

    } type;

#if (NGX_SUPPRESS_WARN)

    fd = NGX_INVALID_FILE;

    prev = NULL;

#endif

    if (filename) {

        /* open configuration file */

        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

        if (fd == NGX_INVALID_FILE) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                               ngx_open_file_n " \"%s\" failed",

                               filename->data);

            return NGX_CONF_ERROR;

        }

        prev = cf->conf_file;

        cf->conf_file = &conf_file;

        if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,

                          ngx_fd_info_n " \"%s\" failed", filename->data);

        }

        cf->conf_file->buffer = &buf;

        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);

        if (buf.start == NULL) {

            goto failed;

        }

        buf.pos = buf.start;

        buf.last = buf.start;

        buf.end = buf.last + NGX_CONF_BUFFER;

        buf.temporary = 1;

        cf->conf_file->file.fd = fd;

        cf->conf_file->file.name.len = filename->len;

        cf->conf_file->file.name.data = filename->data;

        cf->conf_file->file.offset = 0;

        cf->conf_file->file.log = cf->log;

        cf->conf_file->line = 1;

        type = parse_file;

    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {

        type = parse_block;

    } else {

        type = parse_param;

    }

    for ( ;; ) {

        rc = ngx_conf_read_token(cf);

        /*

         * ngx_conf_read_token() may return

         *

         *    NGX_ERROR             there is error

         *    NGX_OK                the token terminated by ";" was found

         *    NGX_CONF_BLOCK_START  the token terminated by "{" was found

         *    NGX_CONF_BLOCK_DONE   the "}" was found

         *    NGX_CONF_FILE_DONE    the configuration file is done

         */

        if (rc == NGX_ERROR) {

            goto done;

        }

        if (rc == NGX_CONF_BLOCK_DONE) {

            if (type != parse_block) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");

                goto failed;

            }

            goto done;

        }

        if (rc == NGX_CONF_FILE_DONE) {

            if (type == parse_block) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "unexpected end of file, expecting \"}\"");

                goto failed;

            }

            goto done;

        }

        if (rc == NGX_CONF_BLOCK_START) {

            if (type == parse_param) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "block directives are not supported "

                                   "in -g option");

                goto failed;

            }

        }

        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */

        if (cf->handler) {

            /*

             * the custom handler, i.e., that is used in the http's

             * "types { ... }" directive

             */

            if (rc == NGX_CONF_BLOCK_START) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\"");

                goto failed;

            }

            rv = (*cf->handler)(cf, NULL, cf->handler_conf);

            if (rv == NGX_CONF_OK) {

                continue;

            }

            if (rv == NGX_CONF_ERROR) {

                goto failed;

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);

            goto failed;

        }

        rc = ngx_conf_handler(cf, rc);

        if (rc == NGX_ERROR) {

            goto failed;

        }

    }

failed:

    rc = NGX_ERROR;

done:

    if (filename) {

        if (cf->conf_file->buffer->start) {

            ngx_free(cf->conf_file->buffer->start);

        }

        if (ngx_close_file(fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_close_file_n " %s failed",

                          filename->data);

            rc = NGX_ERROR;

        }

        cf->conf_file = prev;

    }

    if (rc == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)

{

    char           *rv;

    void           *conf, **confp;

    ngx_uint_t      i, found;

    ngx_str_t      *name;

    ngx_command_t  *cmd;

    name = cf->args->elts;

    found = 0;

    for (i = 0; ngx_modules[i]; i++) {

        cmd = ngx_modules[i]->commands;

        if (cmd == NULL) {

            continue;

        }

        for ( /* void */ ; cmd->name.len; cmd++) {

            if (name->len != cmd->name.len) {

                continue;

            }

            if (ngx_strcmp(name->data, cmd->name.data) != 0) {

                continue;

            }

            found = 1;

            if (ngx_modules[i]->type != NGX_CONF_MODULE

                && ngx_modules[i]->type != cf->module_type)

            {

                continue;

            }

            /* is the directive's location right ? */

            if (!(cmd->type & cf->cmd_type)) {

                continue;

            }

            if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                  "directive \"%s\" is not terminated by \";\"",

                                  name->data);

                return NGX_ERROR;

            }

            if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "directive \"%s\" has no opening \"{\"",

                                   name->data);

                return NGX_ERROR;

            }

            /* is the directive's argument count right ? */

            if (!(cmd->type & NGX_CONF_ANY)) {

                if (cmd->type & NGX_CONF_FLAG) {

                    if (cf->args->nelts != 2) {

                        goto invalid;

                    }

                } else if (cmd->type & NGX_CONF_1MORE) {

                    if (cf->args->nelts < 2) {

                        goto invalid;

                    }

                } else if (cmd->type & NGX_CONF_2MORE) {

                    if (cf->args->nelts < 3) {

                        goto invalid;

                    }

                } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {

                    goto invalid;

                } else if (!(cmd->type & argument_number[cf->args->nelts - 1]))

                {

                    goto invalid;

                }

            }

            /* set up the directive's configuration context */

            conf = NULL;

            if (cmd->type & NGX_DIRECT_CONF) {

                conf = ((void **) cf->ctx)[ngx_modules[i]->index];

            } else if (cmd->type & NGX_MAIN_CONF) {

                conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);

            } else if (cf->ctx) {

                confp = *(void **) ((char *) cf->ctx + cmd->conf);

                if (confp) {

                    conf = confp[ngx_modules[i]->ctx_index];

                }

            }

            rv = cmd->set(cf, cmd, conf);

            if (rv == NGX_CONF_OK) {

                return NGX_OK;

            }

            if (rv == NGX_CONF_ERROR) {

                return NGX_ERROR;

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"%s\" directive %s", name->data, rv);

            return NGX_ERROR;

        }

    }

    if (found) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%s\" directive is not allowed here", name->data);

        return NGX_ERROR;

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "unknown directive \"%s\"", name->data);

    return NGX_ERROR;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid number of arguments in \"%s\" directive",

                       name->data);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_conf_read_token(ngx_conf_t *cf)

{

    u_char      *start, ch, *src, *dst;

    off_t        file_size;

    size_t       len;

    ssize_t      n, size;

    ngx_uint_t   found, need_space, last_space, sharp_comment, variable;

    ngx_uint_t   quoted, s_quoted, d_quoted, start_line;

    ngx_str_t   *word;

    ngx_buf_t   *b;

    found = 0;

    need_space = 0;

    last_space = 1;

    sharp_comment = 0;

    variable = 0;

    quoted = 0;

    s_quoted = 0;

    d_quoted = 0;

    cf->args->nelts = 0;

    b = cf->conf_file->buffer;

    start = b->pos;

    start_line = cf->conf_file->line;

    file_size = ngx_file_size(&cf->conf_file->file.info);

    for ( ;; ) {

        if (b->pos >= b->last) {

            if (cf->conf_file->file.offset >= file_size) {

                if (cf->args->nelts > 0 || !last_space) {

                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {

                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                           "unexpected end of parameter, "

                                           "expecting \";\"");

                        return NGX_ERROR;

                    }

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                  "unexpected end of file, "

                                  "expecting \";\" or \"}\"");

                    return NGX_ERROR;

                }

                return NGX_CONF_FILE_DONE;

            }

            len = b->pos - start;

            if (len == NGX_CONF_BUFFER) {

                cf->conf_file->line = start_line;

                if (d_quoted) {

                    ch = '"';

                } else if (s_quoted) {

                    ch = '\'';

                } else {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "too long parameter \"%*s...\" started",

                                       10, start);

                    return NGX_ERROR;

                }

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "too long parameter, probably "

                                   "missing terminating \"%c\" character", ch);

                return NGX_ERROR;

            }

            if (len) {

                ngx_memmove(b->start, start, len);

            }

            size = (ssize_t) (file_size - cf->conf_file->file.offset);

            if (size > b->end - (b->start + len)) {

                size = b->end - (b->start + len);

            }

            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,

                              cf->conf_file->file.offset);

            if (n == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (n != size) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   ngx_read_file_n " returned "

                                   "only %z bytes instead of %z",

                                   n, size);

                return NGX_ERROR;

            }

            b->pos = b->start + len;

            b->last = b->pos + n;

            start = b->start;

        }

        ch = *b->pos++;

        if (ch == LF) {

            cf->conf_file->line++;

            if (sharp_comment) {

                sharp_comment = 0;

            }

        }

        if (sharp_comment) {

            continue;

        }

        if (quoted) {

            quoted = 0;

            continue;

        }

        if (need_space) {

            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {

                last_space = 1;

                need_space = 0;

                continue;

            }

            if (ch == ';') {

                return NGX_OK;

            }

            if (ch == '{') {

                return NGX_CONF_BLOCK_START;

            }

            if (ch == ')') {

                last_space = 1;

                need_space = 0;

            } else {

                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                    "unexpected \"%c\"", ch);

                 return NGX_ERROR;

            }

        }

        if (last_space) {

            if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {

                continue;

            }

            start = b->pos - 1;

            start_line = cf->conf_file->line;

            switch (ch) {

            case ';':

            case '{':

                if (cf->args->nelts == 0) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "unexpected \"%c\"", ch);

                    return NGX_ERROR;

                }

                if (ch == '{') {

                    return NGX_CONF_BLOCK_START;

                }

                return NGX_OK;

            case '}':

                if (cf->args->nelts != 0) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "unexpected \"}\"");

                    return NGX_ERROR;

                }

                return NGX_CONF_BLOCK_DONE;

            case '#':

                sharp_comment = 1;

                continue;

            case '\\':

                quoted = 1;

                last_space = 0;

                continue;

            case '"':

                start++;

                d_quoted = 1;

                last_space = 0;

                continue;

            case '\'':

                start++;

                s_quoted = 1;

                last_space = 0;

                continue;

            default:

                last_space = 0;

            }

        } else {

            if (ch == '{' && variable) {

                continue;

            }

            variable = 0;

            if (ch == '\\') {

                quoted = 1;

                continue;

            }

            if (ch == '$') {

                variable = 1;

                continue;

            }

            if (d_quoted) {

                if (ch == '"') {

                    d_quoted = 0;

                    need_space = 1;

                    found = 1;

                }

            } else if (s_quoted) {

                if (ch == '\'') {

                    s_quoted = 0;

                    need_space = 1;

                    found = 1;

                }

            } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF

                       || ch == ';' || ch == '{')

            {

                last_space = 1;

                found = 1;

            }

            if (found) {

                word = ngx_array_push(cf->args);

                if (word == NULL) {

                    return NGX_ERROR;

                }

                word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);

                if (word->data == NULL) {

                    return NGX_ERROR;

                }

                for (dst = word->data, src = start, len = 0;

                     src < b->pos - 1;

                     len++)

                {

                    if (*src == '\\') {

                        switch (src[1]) {

                        case '"':

                        case '\'':

                        case '\\':

                            src++;

                            break;

                        case 't':

                            *dst++ = '\t';

                            src += 2;

                            continue;

                        case 'r':

                            *dst++ = '\r';

                            src += 2;

                            continue;

                        case 'n':

                            *dst++ = '\n';

                            src += 2;

                            continue;

                        }

                    }

                    *dst++ = *src++;

                }

                *dst = '\0';

                word->len = len;

                if (ch == ';') {

                    return NGX_OK;

                }

                if (ch == '{') {

                    return NGX_CONF_BLOCK_START;

                }

                found = 0;

            }

        }

    }

}

char *

‌ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char        *rv;

    ngx_int_t    n;

    ngx_str_t   *value, file, name;

    ngx_glob_t   gl;

    value = cf->args->elts;

    file = value[1];

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (strpbrk((char *) file.data, "*?[") == NULL) {

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

        return ngx_conf_parse(cf, &file);

    }

    ngx_memzero(&gl, sizeof(ngx_glob_t));

    gl.pattern = file.data;

    gl.log = cf->log;

    gl.test = 1;

    if (ngx_open_glob(&gl) != NGX_OK) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                           ngx_open_glob_n " \"%s\" failed", file.data);

        return NGX_CONF_ERROR;

    }

    rv = NGX_CONF_OK;

    for ( ;; ) {

        n = ngx_read_glob(&gl, &name);

        if (n != NGX_OK) {

            break;

        }

        file.len = name.len++;

        file.data = ngx_pstrdup(cf->pool, &name);

        if (file.data == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

        rv = ngx_conf_parse(cf, &file);

        if (rv != NGX_CONF_OK) {

            break;

        }

    }

    ngx_close_glob(&gl);

    return rv;

}

ngx_int_t

‌ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix)

{

    ngx_str_t  *prefix;

    prefix = conf_prefix ? &cycle->conf_prefix : &cycle->prefix;

    return ngx_get_full_name(cycle->pool, prefix, name);

}

ngx_open_file_t *

‌ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name)

{

    ngx_str_t         full;

    ngx_uint_t        i;

    ngx_list_part_t  *part;

    ngx_open_file_t  *file;

#if (NGX_SUPPRESS_WARN)

    ngx_str_null(&full);

#endif

    if (name->len) {

        full = *name;

        if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {

            return NULL;

        }

        part = &cycle->open_files.part;

        file = part->elts;

        for (i = 0; /* void */ ; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                file = part->elts;

                i = 0;

            }

            if (full.len != file[i].name.len) {

                continue;

            }

            if (ngx_strcmp(full.data, file[i].name.data) == 0) {

                return &file[i];

            }

        }

    }

    file = ngx_list_push(&cycle->open_files);

    if (file == NULL) {

        return NULL;

    }

    if (name->len) {

        file->fd = NGX_INVALID_FILE;

        file->name = full;

    } else {

        file->fd = ngx_stderr;

        file->name = *name;

    }

    file->flush = NULL;

    file->data = NULL;

    return file;

}

static void

‌ngx_conf_flush_files(ngx_cycle_t *cycle)

{

    ngx_uint_t        i;

    ngx_list_part_t  *part;

    ngx_open_file_t  *file;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "flush files");

    part = &cycle->open_files.part;

    file = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            file = part->elts;

            i = 0;

        }

        if (file[i].flush) {

            file[i].flush(&file[i], cycle->log);

        }

    }

}

void ngx_cdecl

‌ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err,

    const char *fmt, ...)

{

    u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;

    va_list  args;

    last = errstr + NGX_MAX_CONF_ERRSTR;

    va_start(args, fmt);

    p = ngx_vslprintf(errstr, last, fmt, args);

    va_end(args);

    if (err) {

        p = ngx_log_errno(p, last, err);

    }

    if (cf->conf_file == NULL) {

        ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr);

        return;

    }

    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {

        ngx_log_error(level, cf->log, 0, "%*s in command line",

                      p - errstr, errstr);

        return;

    }

    ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",

                  p - errstr, errstr,

                  cf->conf_file->file.name.data, cf->conf_file->line);

}

char *

‌ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t        *value;

    ngx_flag_t       *fp;

    ngx_conf_post_t  *post;

    fp = (ngx_flag_t *) (p + cmd->offset);

    if (*fp != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) {

        *fp = 1;

    } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) {

        *fp = 0;

    } else {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                     "invalid value \"%s\" in \"%s\" directive, "

                     "it must be \"on\" or \"off\"",

                     value[1].data, cmd->name.data);

        return NGX_CONF_ERROR;

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, fp);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t        *field, *value;

    ngx_conf_post_t  *post;

    field = (ngx_str_t *) (p + cmd->offset);

    if (field->data) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *field = value[1];

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, field);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t         *value, *s;

    ngx_array_t      **a;

    ngx_conf_post_t   *post;

    a = (ngx_array_t **) (p + cmd->offset);

    if (*a == NGX_CONF_UNSET_PTR) {

        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));

        if (*a == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    s = ngx_array_push(*a);

    if (s == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    *s = value[1];

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, s);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t         *value;

    ngx_array_t      **a;

    ngx_keyval_t      *kv;

    ngx_conf_post_t   *post;

    a = (ngx_array_t **) (p + cmd->offset);

    if (*a == NULL) {

        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t));

        if (*a == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    kv = ngx_array_push(*a);

    if (kv == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    kv->key = value[1];

    kv->value = value[2];

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, kv);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_int_t        *np;

    ngx_str_t        *value;

    ngx_conf_post_t  *post;

    np = (ngx_int_t *) (p + cmd->offset);

    if (*np != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *np = ngx_atoi(value[1].data, value[1].len);

    if (*np == NGX_ERROR) {

        return "invalid number";

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, np);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    size_t           *sp;

    ngx_str_t        *value;

    ngx_conf_post_t  *post;

    sp = (size_t *) (p + cmd->offset);

    if (*sp != NGX_CONF_UNSET_SIZE) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *sp = ngx_parse_size(&value[1]);

    if (*sp == (size_t) NGX_ERROR) {

        return "invalid value";

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, sp);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    off_t            *op;

    ngx_str_t        *value;

    ngx_conf_post_t  *post;

    op = (off_t *) (p + cmd->offset);

    if (*op != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *op = ngx_parse_offset(&value[1]);

    if (*op == (off_t) NGX_ERROR) {

        return "invalid value";

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, op);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_msec_t       *msp;

    ngx_str_t        *value;

    ngx_conf_post_t  *post;

    msp = (ngx_msec_t *) (p + cmd->offset);

    if (*msp != NGX_CONF_UNSET_MSEC) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *msp = ngx_parse_time(&value[1], 0);

    if (*msp == (ngx_msec_t) NGX_ERROR) {

        return "invalid value";

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, msp);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    time_t           *sp;

    ngx_str_t        *value;

    ngx_conf_post_t  *post;

    sp = (time_t *) (p + cmd->offset);

    if (*sp != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    *sp = ngx_parse_time(&value[1], 1);

    if (*sp == (time_t) NGX_ERROR) {

        return "invalid value";

    }

    if (cmd->post) {

        post = cmd->post;

        return post->post_handler(cf, post, sp);

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char *p = conf;

    ngx_str_t   *value;

    ngx_bufs_t  *bufs;

    bufs = (ngx_bufs_t *) (p + cmd->offset);

    if (bufs->num) {

        return "is duplicate";

    }

    value = cf->args->elts;

    bufs->num = ngx_atoi(value[1].data, value[1].len);

    if (bufs->num == NGX_ERROR || bufs->num == 0) {

        return "invalid value";

    }

    bufs->size = ngx_parse_size(&value[2]);

    if (bufs->size == (size_t) NGX_ERROR || bufs->size == 0) {

        return "invalid value";

    }

    return NGX_CONF_OK;

}

char *

‌ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_uint_t       *np, i;

    ngx_str_t        *value;

    ngx_conf_enum_t  *e;

    np = (ngx_uint_t *) (p + cmd->offset);

    if (*np != NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    value = cf->args->elts;

    e = cmd->post;

    for (i = 0; e[i].name.len != 0; i++) {

        if (e[i].name.len != value[1].len

            || ngx_strcasecmp(e[i].name.data, value[1].data) != 0)

        {

            continue;

        }

        *np = e[i].value;

        return NGX_CONF_OK;

    }

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "invalid value \"%s\"", value[1].data);

    return NGX_CONF_ERROR;

}

char *

‌ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_uint_t          *np, i, m;

    ngx_str_t           *value;

    ngx_conf_bitmask_t  *mask;

    np = (ngx_uint_t *) (p + cmd->offset);

    value = cf->args->elts;

    mask = cmd->post;

    for (i = 1; i < cf->args->nelts; i++) {

        for (m = 0; mask[m].name.len != 0; m++) {

            if (mask[m].name.len != value[i].len

                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)

            {

                continue;

            }

            if (*np & mask[m].mask) {

                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                                   "duplicate value \"%s\"", value[i].data);

            } else {

                *np |= mask[m].mask;

            }

            break;

        }

        if (mask[m].name.len == 0) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "invalid value \"%s\"", value[i].data);

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

#if 0

char *

ngx_conf_unsupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    return "unsupported on this platform";

}

#endif

char *

‌ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data)

{

    ngx_conf_deprecated_t  *d = post;

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "the \"%s\" directive is deprecated, "

                       "use the \"%s\" directive instead",

                       d->old_name, d->new_name);

    return NGX_CONF_OK;

}

char *

‌ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data)

{

    ngx_conf_num_bounds_t  *bounds = post;

    ngx_int_t  *np = data;

    if (bounds->high == -1) {

        if (*np >= bounds->low) {

            return NGX_CONF_OK;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "value must be equal to or greater than %i",

                           bounds->low);

        return NGX_CONF_ERROR;

    }

    if (*np >= bounds->low && *np <= bounds->high) {

        return NGX_CONF_OK;

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "value must be between %i and %i",

                       bounds->low, bounds->high);

    return NGX_CONF_ERROR;

}

src/core/ngx_buf.h - nginx-1.7.9

Data types defined

ngx_buf_s

ngx_buf_t

ngx_buf_tag_t

ngx_bufs_t

ngx_chain_s

ngx_chain_writer_ctx_t

ngx_output_chain_aio_pt

ngx_output_chain_ctx_s

ngx_output_chain_ctx_t

ngx_output_chain_filter_pt

Macros defined

NGX_CHAIN_ERROR

_NGX_BUF_H_INCLUDED_

ngx_alloc_buf

ngx_buf_in_memory

ngx_buf_in_memory_only

ngx_buf_size

ngx_buf_special

ngx_buf_sync_only

ngx_calloc_buf

ngx_free_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_BUF_H_INCLUDED_

‌#define _NGX_BUF_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef void *            ngx_buf_tag_t;

‌typedef struct ngx_buf_s  ngx_buf_t;

‌struct ngx_buf_s {

    u_char          *pos;

    u_char          *last;

    off_t            file_pos;

    off_t            file_last;

    u_char          *start;         /* start of buffer */

    u_char          *end;           /* end of buffer */

    ngx_buf_tag_t    tag;

    ngx_file_t      *file;

    ngx_buf_t       *shadow;

    /* the buf's content could be changed */

    unsigned         temporary:1;

    /*

     * the buf's content is in a memory cache or in a read only memory

     * and must not be changed

     */

    unsigned         memory:1;

    /* the buf's content is mmap()ed and must not be changed */

    unsigned         mmap:1;

    unsigned         recycled:1;

    unsigned         in_file:1;

    unsigned         flush:1;

    unsigned         sync:1;

    unsigned         last_buf:1;

    unsigned         last_in_chain:1;

    unsigned         last_shadow:1;

    unsigned         temp_file:1;

    /* STUB */ int   num;

};

‌struct ngx_chain_s {

    ngx_buf_t    *buf;

    ngx_chain_t  *next;

};

typedef struct {

    ngx_int_t    num;

    size_t       size;

‌} ngx_bufs_t;

‌typedef struct ngx_output_chain_ctx_s  ngx_output_chain_ctx_t;

‌typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in);

#if (NGX_HAVE_FILE_AIO)

‌typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx,

    ngx_file_t *file);

#endif

‌struct ngx_output_chain_ctx_s {

    ngx_buf_t                   *buf;

    ngx_chain_t                 *in;

    ngx_chain_t                 *free;

    ngx_chain_t                 *busy;

    unsigned                     sendfile:1;

    unsigned                     directio:1;

#if (NGX_HAVE_ALIGNED_DIRECTIO)

    unsigned                     unaligned:1;

#endif

    unsigned                     need_in_memory:1;

    unsigned                     need_in_temp:1;

#if (NGX_HAVE_FILE_AIO)

    unsigned                     aio:1;

    ngx_output_chain_aio_pt      aio_handler;

#endif

    off_t                        alignment;

    ngx_pool_t                  *pool;

    ngx_int_t                    allocated;

    ngx_bufs_t                   bufs;

    ngx_buf_tag_t                tag;

    ngx_output_chain_filter_pt   output_filter;

    void                        *filter_ctx;

};

typedef struct {

    ngx_chain_t                 *out;

    ngx_chain_t                **last;

    ngx_connection_t            *connection;

    ngx_pool_t                  *pool;

    off_t                        limit;

‌} ngx_chain_writer_ctx_t;

‌#define NGX_CHAIN_ERROR     (ngx_chain_t *) NGX_ERROR

‌#define ngx_buf_in_memory(b)        (b->temporary || b->memory || b->mmap)

‌#define ngx_buf_in_memory_only(b)   (ngx_buf_in_memory(b) && !b->in_file)

‌#define ngx_buf_special(b)                                                   \

    ((b->flush || b->last_buf || b->sync)                                    \

     && !ngx_buf_in_memory(b) && !b->in_file)

‌#define ngx_buf_sync_only(b)                                                 \

    (b->sync                                                                 \

     && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf)

‌#define ngx_buf_size(b)                                                      \

    (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos):                      \

                            (b->file_last - b->file_pos))

ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);

ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);

‌#define ngx_alloc_buf(pool)  ngx_palloc(pool, sizeof(ngx_buf_t))

‌#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))

ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);

‌#define ngx_free_chain(pool, cl)                                             \

    cl->next = pool->chain;                                                  \

    pool->chain = cl

ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in);

ngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in);

ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,

    ngx_chain_t *in);

ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free);

void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free,

    ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag);

off_t ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit);

ngx_chain_t *ngx_chain_update_sent(ngx_chain_t *in, off_t sent);

#endif /* _NGX_BUF_H_INCLUDED_ */

src/core/ngx_buf.c - nginx-1.7.9

Functions defined

ngx_alloc_chain_link

ngx_chain_add_copy

ngx_chain_coalesce_file

ngx_chain_get_free_buf

ngx_chain_update_chains

ngx_chain_update_sent

ngx_create_chain_of_bufs

ngx_create_temp_buf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

ngx_buf_t *

‌ngx_create_temp_buf(ngx_pool_t *pool, size_t size)

{

    ngx_buf_t *b;

    b = ngx_calloc_buf(pool);

    if (b == NULL) {

        return NULL;

    }

    b->start = ngx_palloc(pool, size);

    if (b->start == NULL) {

        return NULL;

    }

    /*

     * set by ngx_calloc_buf():

     *

     *     b->file_pos = 0;

     *     b->file_last = 0;

     *     b->file = NULL;

     *     b->shadow = NULL;

     *     b->tag = 0;

     *     and flags

     */

    b->pos = b->start;

    b->last = b->start;

    b->end = b->last + size;

    b->temporary = 1;

    return b;

}

ngx_chain_t *

‌ngx_alloc_chain_link(ngx_pool_t *pool)

{

    ngx_chain_t  *cl;

    cl = pool->chain;

    if (cl) {

        pool->chain = cl->next;

        return cl;

    }

    cl = ngx_palloc(pool, sizeof(ngx_chain_t));

    if (cl == NULL) {

        return NULL;

    }

    return cl;

}

ngx_chain_t *

‌ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)

{

    u_char       *p;

    ngx_int_t     i;

    ngx_buf_t    *b;

    ngx_chain_t  *chain, *cl, **ll;

    p = ngx_palloc(pool, bufs->num * bufs->size);

    if (p == NULL) {

        return NULL;

    }

    ll = &chain;

    for (i = 0; i < bufs->num; i++) {

        b = ngx_calloc_buf(pool);

        if (b == NULL) {

            return NULL;

        }

        /*

         * set by ngx_calloc_buf():

         *

         *     b->file_pos = 0;

         *     b->file_last = 0;

         *     b->file = NULL;

         *     b->shadow = NULL;

         *     b->tag = 0;

         *     and flags

         *

         */

        b->pos = p;

        b->last = p;

        b->temporary = 1;

        b->start = p;

        p += bufs->size;

        b->end = p;

        cl = ngx_alloc_chain_link(pool);

        if (cl == NULL) {

            return NULL;

        }

        cl->buf = b;

        *ll = cl;

        ll = &cl->next;

    }

    *ll = NULL;

    return chain;

}

ngx_int_t

‌ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)

{

    ngx_chain_t  *cl, **ll;

    ll = chain;

    for (cl = *chain; cl; cl = cl->next) {

        ll = &cl->next;

    }

    while (in) {

        cl = ngx_alloc_chain_link(pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = in->buf;

        *ll = cl;

        ll = &cl->next;

        in = in->next;

    }

    *ll = NULL;

    return NGX_OK;

}

ngx_chain_t *

‌ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)

{

    ngx_chain_t  *cl;

    if (*free) {

        cl = *free;

        *free = cl->next;

        cl->next = NULL;

        return cl;

    }

    cl = ngx_alloc_chain_link(p);

    if (cl == NULL) {

        return NULL;

    }

    cl->buf = ngx_calloc_buf(p);

    if (cl->buf == NULL) {

        return NULL;

    }

    cl->next = NULL;

    return cl;

}

void

‌ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,

    ngx_chain_t **out, ngx_buf_tag_t tag)

{

    ngx_chain_t  *cl;

    if (*busy == NULL) {

        *busy = *out;

    } else {

        for (cl = *busy; cl->next; cl = cl->next) { /* void */ }

        cl->next = *out;

    }

    *out = NULL;

    while (*busy) {

        cl = *busy;

        if (ngx_buf_size(cl->buf) != 0) {

            break;

        }

        if (cl->buf->tag != tag) {

            *busy = cl->next;

            ngx_free_chain(p, cl);

            continue;

        }

        cl->buf->pos = cl->buf->start;

        cl->buf->last = cl->buf->start;

        *busy = cl->next;

        cl->next = *free;

        *free = cl;

    }

}

off_t

‌ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit)

{

    off_t         total, size, aligned, fprev;

    ngx_fd_t      fd;

    ngx_chain_t  *cl;

    total = 0;

    cl = *in;

    fd = cl->buf->file->fd;

    do {

        size = cl->buf->file_last - cl->buf->file_pos;

        if (size > limit - total) {

            size = limit - total;

            aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)

                       & ~((off_t) ngx_pagesize - 1);

            if (aligned <= cl->buf->file_last) {

                size = aligned - cl->buf->file_pos;

            }

        }

        total += size;

        fprev = cl->buf->file_pos + size;

        cl = cl->next;

    } while (cl

             && cl->buf->in_file

             && total < limit

             && fd == cl->buf->file->fd

             && fprev == cl->buf->file_pos);

    *in = cl;

    return total;

}

ngx_chain_t *

‌ngx_chain_update_sent(ngx_chain_t *in, off_t sent)

{

    off_t  size;

    for ( /* void */ ; in; in = in->next) {

        if (ngx_buf_special(in->buf)) {

            continue;

        }

        if (sent == 0) {

            break;

        }

        size = ngx_buf_size(in->buf);

        if (sent >= size) {

            sent -= size;

            if (ngx_buf_in_memory(in->buf)) {

                in->buf->pos = in->buf->last;

            }

            if (in->buf->in_file) {

                in->buf->file_pos = in->buf->file_last;

            }

            continue;

        }

        if (ngx_buf_in_memory(in->buf)) {

            in->buf->pos += (size_t) sent;

        }

        if (in->buf->in_file) {

            in->buf->file_pos += sent;

        }

        break;

    }

    return in;

}

src/core/ngx_output_chain.c - nginx-1.7.9

Functions defined

ngx_chain_writer

ngx_output_chain

ngx_output_chain_add_copy

ngx_output_chain_align_file_buf

ngx_output_chain_as_is

ngx_output_chain_copy_buf

ngx_output_chain_get_buf

Macros defined

NGX_NONE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if 0

#define NGX_SENDFILE_LIMIT  4096

#endif

/*

* When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly

* to an application memory from a device if parameters are aligned

* to device sector boundary (512 bytes).  They fallback to usual read

* operation if the parameters are not aligned.

* Linux allows DIRECTIO only if the parameters are aligned to a filesystem

* sector boundary, otherwise it returns EINVAL.  The sector size is

* usually 512 bytes, however, on XFS it may be 4096 bytes.

*/

‌#define NGX_NONE            1

static ngx_inline ngx_int_t

    ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);

static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,

    ngx_chain_t **chain, ngx_chain_t *in);

static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,

    off_t bsize);

static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,

    off_t bsize);

static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);

ngx_int_t

‌ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)

{

    off_t         bsize;

    ngx_int_t     rc, last;

    ngx_chain_t  *cl, *out, **last_out;

    if (ctx->in == NULL && ctx->busy == NULL) {

        /*

         * the short path for the case when the ctx->in and ctx->busy chains

         * are empty, the incoming chain is empty too or has the single buf

         * that does not require the copy

         */

        if (in == NULL) {

            return ctx->output_filter(ctx->filter_ctx, in);

        }

        if (in->next == NULL

#if (NGX_SENDFILE_LIMIT)

            && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)

#endif

            && ngx_output_chain_as_is(ctx, in->buf))

        {

            return ctx->output_filter(ctx->filter_ctx, in);

        }

    }

    /* add the incoming buf to the chain ctx->in */

    if (in) {

        if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {

            return NGX_ERROR;

        }

    }

    out = NULL;

    last_out = &out;

    last = NGX_NONE;

    for ( ;; ) {

#if (NGX_HAVE_FILE_AIO)

        if (ctx->aio) {

            return NGX_AGAIN;

        }

#endif

        while (ctx->in) {

            /*

             * cycle while there are the ctx->in bufs

             * and there are the free output bufs to copy in

             */

            bsize = ngx_buf_size(ctx->in->buf);

            if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {

                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,

                              "zero size buf in output "

                              "t:%d r:%d f:%d %p %p-%p %p %O-%O",

                              ctx->in->buf->temporary,

                              ctx->in->buf->recycled,

                              ctx->in->buf->in_file,

                              ctx->in->buf->start,

                              ctx->in->buf->pos,

                              ctx->in->buf->last,

                              ctx->in->buf->file,

                              ctx->in->buf->file_pos,

                              ctx->in->buf->file_last);

                ngx_debug_point();

                ctx->in = ctx->in->next;

                continue;

            }

            if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {

                /* move the chain link to the output chain */

                cl = ctx->in;

                ctx->in = cl->next;

                *last_out = cl;

                last_out = &cl->next;

                cl->next = NULL;

                continue;

            }

            if (ctx->buf == NULL) {

                rc = ngx_output_chain_align_file_buf(ctx, bsize);

                if (rc == NGX_ERROR) {

                    return NGX_ERROR;

                }

                if (rc != NGX_OK) {

                    if (ctx->free) {

                        /* get the free buf */

                        cl = ctx->free;

                        ctx->buf = cl->buf;

                        ctx->free = cl->next;

                        ngx_free_chain(ctx->pool, cl);

                    } else if (out || ctx->allocated == ctx->bufs.num) {

                        break;

                    } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {

                        return NGX_ERROR;

                    }

                }

            }

            rc = ngx_output_chain_copy_buf(ctx);

            if (rc == NGX_ERROR) {

                return rc;

            }

            if (rc == NGX_AGAIN) {

                if (out) {

                    break;

                }

                return rc;

            }

            /* delete the completed buf from the ctx->in chain */

            if (ngx_buf_size(ctx->in->buf) == 0) {

                ctx->in = ctx->in->next;

            }

            cl = ngx_alloc_chain_link(ctx->pool);

            if (cl == NULL) {

                return NGX_ERROR;

            }

            cl->buf = ctx->buf;

            cl->next = NULL;

            *last_out = cl;

            last_out = &cl->next;

            ctx->buf = NULL;

        }

        if (out == NULL && last != NGX_NONE) {

            if (ctx->in) {

                return NGX_AGAIN;

            }

            return last;

        }

        last = ctx->output_filter(ctx->filter_ctx, out);

        if (last == NGX_ERROR || last == NGX_DONE) {

            return last;

        }

        ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,

                                ctx->tag);

        last_out = &out;

    }

}

static ngx_inline ngx_int_t

‌ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)

{

    ngx_uint_t  sendfile;

    if (ngx_buf_special(buf)) {

        return 1;

    }

    if (buf->in_file && buf->file->directio) {

        return 0;

    }

    sendfile = ctx->sendfile;

#if (NGX_SENDFILE_LIMIT)

    if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {

        sendfile = 0;

    }

#endif

    if (!sendfile) {

        if (!ngx_buf_in_memory(buf)) {

            return 0;

        }

        buf->in_file = 0;

    }

    if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {

        return 0;

    }

    if (ctx->need_in_temp && (buf->memory || buf->mmap)) {

        return 0;

    }

    return 1;

}

static ngx_int_t

‌ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,

    ngx_chain_t *in)

{

    ngx_chain_t  *cl, **ll;

#if (NGX_SENDFILE_LIMIT)

    ngx_buf_t    *b, *buf;

#endif

    ll = chain;

    for (cl = *chain; cl; cl = cl->next) {

        ll = &cl->next;

    }

    while (in) {

        cl = ngx_alloc_chain_link(pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

#if (NGX_SENDFILE_LIMIT)

        buf = in->buf;

        if (buf->in_file

            && buf->file_pos < NGX_SENDFILE_LIMIT

            && buf->file_last > NGX_SENDFILE_LIMIT)

        {

            /* split a file buf on two bufs by the sendfile limit */

            b = ngx_calloc_buf(pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(b, buf, sizeof(ngx_buf_t));

            if (ngx_buf_in_memory(buf)) {

                buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);

                b->last = buf->pos;

            }

            buf->file_pos = NGX_SENDFILE_LIMIT;

            b->file_last = NGX_SENDFILE_LIMIT;

            cl->buf = b;

        } else {

            cl->buf = buf;

            in = in->next;

        }

#else

        cl->buf = in->buf;

        in = in->next;

#endif

        cl->next = NULL;

        *ll = cl;

        ll = &cl->next;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)

{

    size_t      size;

    ngx_buf_t  *in;

    in = ctx->in->buf;

    if (in->file == NULL || !in->file->directio) {

        return NGX_DECLINED;

    }

    ctx->directio = 1;

    size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));

    if (size == 0) {

        if (bsize >= (off_t) ctx->bufs.size) {

            return NGX_DECLINED;

        }

        size = (size_t) bsize;

    } else {

        size = (size_t) ctx->alignment - size;

        if ((off_t) size > bsize) {

            size = (size_t) bsize;

        }

    }

    ctx->buf = ngx_create_temp_buf(ctx->pool, size);

    if (ctx->buf == NULL) {

        return NGX_ERROR;

    }

    /*

     * we do not set ctx->buf->tag, because we do not want

     * to reuse the buf via ctx->free list

     */

#if (NGX_HAVE_ALIGNED_DIRECTIO)

    ctx->unaligned = 1;

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)

{

    size_t       size;

    ngx_buf_t   *b, *in;

    ngx_uint_t   recycled;

    in = ctx->in->buf;

    size = ctx->bufs.size;

    recycled = 1;

    if (in->last_in_chain) {

        if (bsize < (off_t) size) {

            /*

             * allocate a small temp buf for a small last buf

             * or its small last part

             */

            size = (size_t) bsize;

            recycled = 0;

        } else if (!ctx->directio

                   && ctx->bufs.num == 1

                   && (bsize < (off_t) (size + size / 4)))

        {

            /*

             * allocate a temp buf that equals to a last buf,

             * if there is no directio, the last buf size is lesser

             * than 1.25 of bufs.size and the temp buf is single

             */

            size = (size_t) bsize;

            recycled = 0;

        }

    }

    b = ngx_calloc_buf(ctx->pool);

    if (b == NULL) {

        return NGX_ERROR;

    }

    if (ctx->directio) {

        /*

         * allocate block aligned to a disk sector size to enable

         * userland buffer direct usage conjunctly with directio

         */

        b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);

        if (b->start == NULL) {

            return NGX_ERROR;

        }

    } else {

        b->start = ngx_palloc(ctx->pool, size);

        if (b->start == NULL) {

            return NGX_ERROR;

        }

    }

    b->pos = b->start;

    b->last = b->start;

    b->end = b->last + size;

    b->temporary = 1;

    b->tag = ctx->tag;

    b->recycled = recycled;

    ctx->buf = b;

    ctx->allocated++;

    return NGX_OK;

}

static ngx_int_t

‌ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)

{

    off_t        size;

    ssize_t      n;

    ngx_buf_t   *src, *dst;

    ngx_uint_t   sendfile;

    src = ctx->in->buf;

    dst = ctx->buf;

    size = ngx_buf_size(src);

    size = ngx_min(size, dst->end - dst->pos);

    sendfile = ctx->sendfile & !ctx->directio;

#if (NGX_SENDFILE_LIMIT)

    if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {

        sendfile = 0;

    }

#endif

    if (ngx_buf_in_memory(src)) {

        ngx_memcpy(dst->pos, src->pos, (size_t) size);

        src->pos += (size_t) size;

        dst->last += (size_t) size;

        if (src->in_file) {

            if (sendfile) {

                dst->in_file = 1;

                dst->file = src->file;

                dst->file_pos = src->file_pos;

                dst->file_last = src->file_pos + size;

            } else {

                dst->in_file = 0;

            }

            src->file_pos += size;

        } else {

            dst->in_file = 0;

        }

        if (src->pos == src->last) {

            dst->flush = src->flush;

            dst->last_buf = src->last_buf;

            dst->last_in_chain = src->last_in_chain;

        }

    } else {

#if (NGX_HAVE_ALIGNED_DIRECTIO)

        if (ctx->unaligned) {

            if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,

                              ngx_directio_off_n " \"%s\" failed",

                              src->file->name.data);

            }

        }

#endif

#if (NGX_HAVE_FILE_AIO)

        if (ctx->aio_handler) {

            n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,

                                  src->file_pos, ctx->pool);

            if (n == NGX_AGAIN) {

                ctx->aio_handler(ctx, src->file);

                return NGX_AGAIN;

            }

        } else {

            n = ngx_read_file(src->file, dst->pos, (size_t) size,

                              src->file_pos);

        }

#else

        n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);

#endif

#if (NGX_HAVE_ALIGNED_DIRECTIO)

        if (ctx->unaligned) {

            ngx_err_t  err;

            err = ngx_errno;

            if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,

                              ngx_directio_on_n " \"%s\" failed",

                              src->file->name.data);

            }

            ngx_set_errno(err);

            ctx->unaligned = 0;

        }

#endif

        if (n == NGX_ERROR) {

            return (ngx_int_t) n;

        }

        if (n != size) {

            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,

                          ngx_read_file_n " read only %z of %O from \"%s\"",

                          n, size, src->file->name.data);

            return NGX_ERROR;

        }

        dst->last += n;

        if (sendfile) {

            dst->in_file = 1;

            dst->file = src->file;

            dst->file_pos = src->file_pos;

            dst->file_last = src->file_pos + n;

        } else {

            dst->in_file = 0;

        }

        src->file_pos += n;

        if (src->file_pos == src->file_last) {

            dst->flush = src->flush;

            dst->last_buf = src->last_buf;

            dst->last_in_chain = src->last_in_chain;

        }

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_chain_writer(void *data, ngx_chain_t *in)

{

    ngx_chain_writer_ctx_t *ctx = data;

    off_t              size;

    ngx_chain_t       *cl;

    ngx_connection_t  *c;

    c = ctx->connection;

    for (size = 0; in; in = in->next) {

#if 1

        if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {

            ngx_debug_point();

        }

#endif

        size += ngx_buf_size(in->buf);

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,

                       "chain writer buf fl:%d s:%uO",

                       in->buf->flush, ngx_buf_size(in->buf));

        cl = ngx_alloc_chain_link(ctx->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = in->buf;

        cl->next = NULL;

        *ctx->last = cl;

        ctx->last = &cl->next;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,

                   "chain writer in: %p", ctx->out);

    for (cl = ctx->out; cl; cl = cl->next) {

#if 1

        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {

            ngx_debug_point();

        }

#endif

        size += ngx_buf_size(cl->buf);

    }

    if (size == 0 && !c->buffered) {

        return NGX_OK;

    }

    ctx->out = c->send_chain(c, ctx->out, ctx->limit);

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,

                   "chain writer out: %p", ctx->out);

    if (ctx->out == NGX_CHAIN_ERROR) {

        return NGX_ERROR;

    }

    if (ctx->out == NULL) {

        ctx->last = &ctx->out;

        if (!c->buffered) {

            return NGX_OK;

        }

    }

    return NGX_AGAIN;

}

src/http/ngx_http_parse.c - nginx-1.7.9

Global variables defined

usual

Functions defined

ngx_http_arg

ngx_http_parse_chunked

ngx_http_parse_complex_uri

ngx_http_parse_header_line

ngx_http_parse_multi_header_lines

ngx_http_parse_request_line

ngx_http_parse_set_cookie_lines

ngx_http_parse_status_line

ngx_http_parse_unsafe_uri

ngx_http_parse_uri

ngx_http_split_args

Macros defined

ngx_str3Ocmp

ngx_str3Ocmp

ngx_str3_cmp

ngx_str3_cmp

ngx_str4cmp

ngx_str4cmp

ngx_str5cmp

ngx_str5cmp

ngx_str6cmp

ngx_str6cmp

ngx_str7_cmp

ngx_str7_cmp

ngx_str8cmp

ngx_str8cmp

ngx_str9cmp

ngx_str9cmp

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌static uint32_t  usual[] = {

    0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */

                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

    0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */

                /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

#if (NGX_WIN32)

    0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */

#else

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

#endif

                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */

};

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

‌#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

‌#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

‌#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

‌#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \

        && m[4] == c4

‌#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \

        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)

‌#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \

        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)

‌#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \

        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)

‌#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \

    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \

        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \

        && m[8] == c8

#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */

‌#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \

    m[0] == c0 && m[1] == c1 && m[2] == c2

‌#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \

    m[0] == c0 && m[2] == c2 && m[3] == c3

‌#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3

‌#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4

‌#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \

        && m[4] == c4 && m[5] == c5

‌#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \

        && m[4] == c4 && m[5] == c5 && m[6] == c6

‌#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \

        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7

‌#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \

        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8

#endif

/* gcc, icc, msvc and others compile these switches as an jump table */

ngx_int_t

‌ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)

{

    u_char  c, ch, *p, *m;

    enum {

        sw_start = 0,

        sw_method,

        sw_spaces_before_uri,

        sw_schema,

        sw_schema_slash,

        sw_schema_slash_slash,

        sw_host_start,

        sw_host,

        sw_host_end,

        sw_host_ip_literal,

        sw_port,

        sw_host_http_09,

        sw_after_slash_in_uri,

        sw_check_uri,

        sw_check_uri_http_09,

        sw_uri,

        sw_http_09,

        sw_http_H,

        sw_http_HT,

        sw_http_HTT,

        sw_http_HTTP,

        sw_first_major_digit,

        sw_major_digit,

        sw_first_minor_digit,

        sw_minor_digit,

        sw_spaces_after_digit,

        sw_almost_done

    } state;

    state = r->state;

    for (p = b->pos; p < b->last; p++) {

        ch = *p;

        switch (state) {

        /* HTTP methods: GET, HEAD, POST */

        case sw_start:

            r->request_start = p;

            if (ch == CR || ch == LF) {

                break;

            }

            if ((ch < 'A' || ch > 'Z') && ch != '_') {

                return NGX_HTTP_PARSE_INVALID_METHOD;

            }

            state = sw_method;

            break;

        case sw_method:

            if (ch == ' ') {

                r->method_end = p - 1;

                m = r->request_start;

                switch (p - m) {

                case 3:

                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {

                        r->method = NGX_HTTP_GET;

                        break;

                    }

                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {

                        r->method = NGX_HTTP_PUT;

                        break;

                    }

                    break;

                case 4:

                    if (m[1] == 'O') {

                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {

                            r->method = NGX_HTTP_POST;

                            break;

                        }

                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {

                            r->method = NGX_HTTP_COPY;

                            break;

                        }

                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {

                            r->method = NGX_HTTP_MOVE;

                            break;

                        }

                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {

                            r->method = NGX_HTTP_LOCK;

                            break;

                        }

                    } else {

                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {

                            r->method = NGX_HTTP_HEAD;

                            break;

                        }

                    }

                    break;

                case 5:

                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {

                        r->method = NGX_HTTP_MKCOL;

                        break;

                    }

                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {

                        r->method = NGX_HTTP_PATCH;

                        break;

                    }

                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {

                        r->method = NGX_HTTP_TRACE;

                        break;

                    }

                    break;

                case 6:

                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {

                        r->method = NGX_HTTP_DELETE;

                        break;

                    }

                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {

                        r->method = NGX_HTTP_UNLOCK;

                        break;

                    }

                    break;

                case 7:

                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))

                    {

                        r->method = NGX_HTTP_OPTIONS;

                    }

                    break;

                case 8:

                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))

                    {

                        r->method = NGX_HTTP_PROPFIND;

                    }

                    break;

                case 9:

                    if (ngx_str9cmp(m,

                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))

                    {

                        r->method = NGX_HTTP_PROPPATCH;

                    }

                    break;

                }

                state = sw_spaces_before_uri;

                break;

            }

            if ((ch < 'A' || ch > 'Z') && ch != '_') {

                return NGX_HTTP_PARSE_INVALID_METHOD;

            }

            break;

        /* space* before URI */

        case sw_spaces_before_uri:

            if (ch == '/') {

                r->uri_start = p;

                state = sw_after_slash_in_uri;

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                r->schema_start = p;

                state = sw_schema;

                break;

            }

            switch (ch) {

            case ' ':

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_schema:

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                break;

            }

            switch (ch) {

            case ':':

                r->schema_end = p;

                state = sw_schema_slash;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_schema_slash:

            switch (ch) {

            case '/':

                state = sw_schema_slash_slash;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_schema_slash_slash:

            switch (ch) {

            case '/':

                state = sw_host_start;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_host_start:

            r->host_start = p;

            if (ch == '[') {

                state = sw_host_ip_literal;

                break;

            }

            state = sw_host;

            /* fall through */

        case sw_host:

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                break;

            }

            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {

                break;

            }

            /* fall through */

        case sw_host_end:

            r->host_end = p;

            switch (ch) {

            case ':':

                state = sw_port;

                break;

            case '/':

                r->uri_start = p;

                state = sw_after_slash_in_uri;

                break;

            case ' ':

                /*

                 * use single "/" from request line to preserve pointers,

                 * if request line will be copied to large client buffer

                 */

                r->uri_start = r->schema_end + 1;

                r->uri_end = r->schema_end + 2;

                state = sw_host_http_09;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_host_ip_literal:

            if (ch >= '0' && ch <= '9') {

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                break;

            }

            switch (ch) {

            case ':':

                break;

            case ']':

                state = sw_host_end;

                break;

            case '-':

            case '.':

            case '_':

            case '~':

                /* unreserved */

                break;

            case '!':

            case '$':

            case '&':

            case '\'':

            case '(':

            case ')':

            case '*':

            case '+':

            case ',':

            case ';':

            case '=':

                /* sub-delims */

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_port:

            if (ch >= '0' && ch <= '9') {

                break;

            }

            switch (ch) {

            case '/':

                r->port_end = p;

                r->uri_start = p;

                state = sw_after_slash_in_uri;

                break;

            case ' ':

                r->port_end = p;

                /*

                 * use single "/" from request line to preserve pointers,

                 * if request line will be copied to large client buffer

                 */

                r->uri_start = r->schema_end + 1;

                r->uri_end = r->schema_end + 2;

                state = sw_host_http_09;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* space+ after "http://host[:port] " */

        case sw_host_http_09:

            switch (ch) {

            case ' ':

                break;

            case CR:

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->http_minor = 9;

                goto done;

            case 'H':

                r->http_protocol.data = p;

                state = sw_http_H;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* check "/.", "//", "%", and "\" (Win32) in URI */

        case sw_after_slash_in_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                state = sw_check_uri;

                break;

            }

            switch (ch) {

            case ' ':

                r->uri_end = p;

                state = sw_check_uri_http_09;

                break;

            case CR:

                r->uri_end = p;

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->uri_end = p;

                r->http_minor = 9;

                goto done;

            case '.':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '%':

                r->quoted_uri = 1;

                state = sw_uri;

                break;

            case '/':

                r->complex_uri = 1;

                state = sw_uri;

                break;

#if (NGX_WIN32)

            case '\\':

                r->complex_uri = 1;

                state = sw_uri;

                break;

#endif

            case '?':

                r->args_start = p + 1;

                state = sw_uri;

                break;

            case '#':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '+':

                r->plus_in_uri = 1;

                break;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            default:

                state = sw_check_uri;

                break;

            }

            break;

        /* check "/", "%" and "\" (Win32) in URI */

        case sw_check_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                break;

            }

            switch (ch) {

            case '/':

#if (NGX_WIN32)

                if (r->uri_ext == p) {

                    r->complex_uri = 1;

                    state = sw_uri;

                    break;

                }

#endif

                r->uri_ext = NULL;

                state = sw_after_slash_in_uri;

                break;

            case '.':

                r->uri_ext = p + 1;

                break;

            case ' ':

                r->uri_end = p;

                state = sw_check_uri_http_09;

                break;

            case CR:

                r->uri_end = p;

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->uri_end = p;

                r->http_minor = 9;

                goto done;

#if (NGX_WIN32)

            case '\\':

                r->complex_uri = 1;

                state = sw_after_slash_in_uri;

                break;

#endif

            case '%':

                r->quoted_uri = 1;

                state = sw_uri;

                break;

            case '?':

                r->args_start = p + 1;

                state = sw_uri;

                break;

            case '#':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '+':

                r->plus_in_uri = 1;

                break;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* space+ after URI */

        case sw_check_uri_http_09:

            switch (ch) {

            case ' ':

                break;

            case CR:

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->http_minor = 9;

                goto done;

            case 'H':

                r->http_protocol.data = p;

                state = sw_http_H;

                break;

            default:

                r->space_in_uri = 1;

                state = sw_check_uri;

                p--;

                break;

            }

            break;

        /* URI */

        case sw_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                break;

            }

            switch (ch) {

            case ' ':

                r->uri_end = p;

                state = sw_http_09;

                break;

            case CR:

                r->uri_end = p;

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->uri_end = p;

                r->http_minor = 9;

                goto done;

            case '#':

                r->complex_uri = 1;

                break;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* space+ after URI */

        case sw_http_09:

            switch (ch) {

            case ' ':

                break;

            case CR:

                r->http_minor = 9;

                state = sw_almost_done;

                break;

            case LF:

                r->http_minor = 9;

                goto done;

            case 'H':

                r->http_protocol.data = p;

                state = sw_http_H;

                break;

            default:

                r->space_in_uri = 1;

                state = sw_uri;

                p--;

                break;

            }

            break;

        case sw_http_H:

            switch (ch) {

            case 'T':

                state = sw_http_HT;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_http_HT:

            switch (ch) {

            case 'T':

                state = sw_http_HTT;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_http_HTT:

            switch (ch) {

            case 'P':

                state = sw_http_HTTP;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        case sw_http_HTTP:

            switch (ch) {

            case '/':

                state = sw_first_major_digit;

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* first digit of major HTTP version */

        case sw_first_major_digit:

            if (ch < '1' || ch > '9') {

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            r->http_major = ch - '0';

            state = sw_major_digit;

            break;

        /* major HTTP version or dot */

        case sw_major_digit:

            if (ch == '.') {

                state = sw_first_minor_digit;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            r->http_major = r->http_major * 10 + ch - '0';

            break;

        /* first digit of minor HTTP version */

        case sw_first_minor_digit:

            if (ch < '0' || ch > '9') {

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            r->http_minor = ch - '0';

            state = sw_minor_digit;

            break;

        /* minor HTTP version or end of request line */

        case sw_minor_digit:

            if (ch == CR) {

                state = sw_almost_done;

                break;

            }

            if (ch == LF) {

                goto done;

            }

            if (ch == ' ') {

                state = sw_spaces_after_digit;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            r->http_minor = r->http_minor * 10 + ch - '0';

            break;

        case sw_spaces_after_digit:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

            break;

        /* end of request line */

        case sw_almost_done:

            r->request_end = p - 1;

            switch (ch) {

            case LF:

                goto done;

            default:

                return NGX_HTTP_PARSE_INVALID_REQUEST;

            }

        }

    }

    b->pos = p;

    r->state = state;

    return NGX_AGAIN;

done:

    b->pos = p + 1;

    if (r->request_end == NULL) {

        r->request_end = p;

    }

    r->http_version = r->http_major * 1000 + r->http_minor;

    r->state = sw_start;

    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {

        return NGX_HTTP_PARSE_INVALID_09_METHOD;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_uint_t allow_underscores)

{

    u_char      c, ch, *p;

    ngx_uint_t  hash, i;

    enum {

        sw_start = 0,

        sw_name,

        sw_space_before_value,

        sw_value,

        sw_space_after_value,

        sw_ignore_line,

        sw_almost_done,

        sw_header_almost_done

    } state;

    /* the last '\0' is not needed because string is zero terminated */

    static u_char  lowcase[] =

        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

        "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"

        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"

        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"

        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

    state = r->state;

    hash = r->header_hash;

    i = r->lowcase_index;

    for (p = b->pos; p < b->last; p++) {

        ch = *p;

        switch (state) {

        /* first char */

        case sw_start:

            r->header_name_start = p;

            r->invalid_header = 0;

            switch (ch) {

            case CR:

                r->header_end = p;

                state = sw_header_almost_done;

                break;

            case LF:

                r->header_end = p;

                goto header_done;

            default:

                state = sw_name;

                c = lowcase[ch];

                if (c) {

                    hash = ngx_hash(0, c);

                    r->lowcase_header[0] = c;

                    i = 1;

                    break;

                }

                if (ch == '_') {

                    if (allow_underscores) {

                        hash = ngx_hash(0, ch);

                        r->lowcase_header[0] = ch;

                        i = 1;

                    } else {

                        r->invalid_header = 1;

                    }

                    break;

                }

                if (ch == '\0') {

                    return NGX_HTTP_PARSE_INVALID_HEADER;

                }

                r->invalid_header = 1;

                break;

            }

            break;

        /* header name */

        case sw_name:

            c = lowcase[ch];

            if (c) {

                hash = ngx_hash(hash, c);

                r->lowcase_header[i++] = c;

                i &= (NGX_HTTP_LC_HEADER_LEN - 1);

                break;

            }

            if (ch == '_') {

                if (allow_underscores) {

                    hash = ngx_hash(hash, ch);

                    r->lowcase_header[i++] = ch;

                    i &= (NGX_HTTP_LC_HEADER_LEN - 1);

                } else {

                    r->invalid_header = 1;

                }

                break;

            }

            if (ch == ':') {

                r->header_name_end = p;

                state = sw_space_before_value;

                break;

            }

            if (ch == CR) {

                r->header_name_end = p;

                r->header_start = p;

                r->header_end = p;

                state = sw_almost_done;

                break;

            }

            if (ch == LF) {

                r->header_name_end = p;

                r->header_start = p;

                r->header_end = p;

                goto done;

            }

            /* IIS may send the duplicate "HTTP/1.1 ..." lines */

            if (ch == '/'

                && r->upstream

                && p - r->header_name_start == 4

                && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)

            {

                state = sw_ignore_line;

                break;

            }

            if (ch == '\0') {

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

            r->invalid_header = 1;

            break;

        /* space* before header value */

        case sw_space_before_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                r->header_start = p;

                r->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                r->header_start = p;

                r->header_end = p;

                goto done;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_HEADER;

            default:

                r->header_start = p;

                state = sw_value;

                break;

            }

            break;

        /* header value */

        case sw_value:

            switch (ch) {

            case ' ':

                r->header_end = p;

                state = sw_space_after_value;

                break;

            case CR:

                r->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                r->header_end = p;

                goto done;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

            break;

        /* space* before end of header line */

        case sw_space_after_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            case '\0':

                return NGX_HTTP_PARSE_INVALID_HEADER;

            default:

                state = sw_value;

                break;

            }

            break;

        /* ignore header line */

        case sw_ignore_line:

            switch (ch) {

            case LF:

                state = sw_start;

                break;

            default:

                break;

            }

            break;

        /* end of header line */

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            case CR:

                break;

            default:

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

            break;

        /* end of header */

        case sw_header_almost_done:

            switch (ch) {

            case LF:

                goto header_done;

            default:

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

        }

    }

    b->pos = p;

    r->state = state;

    r->header_hash = hash;

    r->lowcase_index = i;

    return NGX_AGAIN;

done:

    b->pos = p + 1;

    r->state = sw_start;

    r->header_hash = hash;

    r->lowcase_index = i;

    return NGX_OK;

header_done:

    b->pos = p + 1;

    r->state = sw_start;

    return NGX_HTTP_PARSE_HEADER_DONE;

}

ngx_int_t

‌ngx_http_parse_uri(ngx_http_request_t *r)

{

    u_char  *p, ch;

    enum {

        sw_start = 0,

        sw_after_slash_in_uri,

        sw_check_uri,

        sw_uri

    } state;

    state = sw_start;

    for (p = r->uri_start; p != r->uri_end; p++) {

        ch = *p;

        switch (state) {

        case sw_start:

            if (ch != '/') {

                return NGX_ERROR;

            }

            state = sw_after_slash_in_uri;

            break;

        /* check "/.", "//", "%", and "\" (Win32) in URI */

        case sw_after_slash_in_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                state = sw_check_uri;

                break;

            }

            switch (ch) {

            case ' ':

                r->space_in_uri = 1;

                state = sw_check_uri;

                break;

            case '.':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '%':

                r->quoted_uri = 1;

                state = sw_uri;

                break;

            case '/':

                r->complex_uri = 1;

                state = sw_uri;

                break;

#if (NGX_WIN32)

            case '\\':

                r->complex_uri = 1;

                state = sw_uri;

                break;

#endif

            case '?':

                r->args_start = p + 1;

                state = sw_uri;

                break;

            case '#':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '+':

                r->plus_in_uri = 1;

                break;

            default:

                state = sw_check_uri;

                break;

            }

            break;

        /* check "/", "%" and "\" (Win32) in URI */

        case sw_check_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                break;

            }

            switch (ch) {

            case '/':

#if (NGX_WIN32)

                if (r->uri_ext == p) {

                    r->complex_uri = 1;

                    state = sw_uri;

                    break;

                }

#endif

                r->uri_ext = NULL;

                state = sw_after_slash_in_uri;

                break;

            case '.':

                r->uri_ext = p + 1;

                break;

            case ' ':

                r->space_in_uri = 1;

                break;

#if (NGX_WIN32)

            case '\\':

                r->complex_uri = 1;

                state = sw_after_slash_in_uri;

                break;

#endif

            case '%':

                r->quoted_uri = 1;

                state = sw_uri;

                break;

            case '?':

                r->args_start = p + 1;

                state = sw_uri;

                break;

            case '#':

                r->complex_uri = 1;

                state = sw_uri;

                break;

            case '+':

                r->plus_in_uri = 1;

                break;

            }

            break;

        /* URI */

        case sw_uri:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                break;

            }

            switch (ch) {

            case ' ':

                r->space_in_uri = 1;

                break;

            case '#':

                r->complex_uri = 1;

                break;

            }

            break;

        }

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)

{

    u_char  c, ch, decoded, *p, *u;

    enum {

        sw_usual = 0,

        sw_slash,

        sw_dot,

        sw_dot_dot,

        sw_quoted,

        sw_quoted_second

    } state, quoted_state;

#if (NGX_SUPPRESS_WARN)

    decoded = '\0';

    quoted_state = sw_usual;

#endif

    state = sw_usual;

    p = r->uri_start;

    u = r->uri.data;

    r->uri_ext = NULL;

    r->args_start = NULL;

    ch = *p++;

    while (p <= r->uri_end) {

        /*

         * we use "ch = *p++" inside the cycle, but this operation is safe,

         * because after the URI there is always at least one character:

         * the line feed

         */

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "s:%d in:'%Xd:%c'", state, ch, ch);

        switch (state) {

        case sw_usual:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                *u++ = ch;

                ch = *p++;

                break;

            }

            switch (ch) {

#if (NGX_WIN32)

            case '\\':

                if (u - 2 >= r->uri.data

                    && *(u - 1) == '.' && *(u - 2) != '.')

                {

                    u--;

                }

                r->uri_ext = NULL;

                if (p == r->uri_start + r->uri.len) {

                    /*

                     * we omit the last "\" to cause redirect because

                     * the browsers do not treat "\" as "/" in relative URL path

                     */

                    break;

                }

                state = sw_slash;

                *u++ = '/';

                break;

#endif

            case '/':

#if (NGX_WIN32)

                if (u - 2 >= r->uri.data

                    && *(u - 1) == '.' && *(u - 2) != '.')

                {

                    u--;

                }

#endif

                r->uri_ext = NULL;

                state = sw_slash;

                *u++ = ch;

                break;

            case '%':

                quoted_state = state;

                state = sw_quoted;

                break;

            case '?':

                r->args_start = p;

                goto args;

            case '#':

                goto done;

            case '.':

                r->uri_ext = u + 1;

                *u++ = ch;

                break;

            case '+':

                r->plus_in_uri = 1;

                /* fall through */

            default:

                *u++ = ch;

                break;

            }

            ch = *p++;

            break;

        case sw_slash:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                state = sw_usual;

                *u++ = ch;

                ch = *p++;

                break;

            }

            switch (ch) {

#if (NGX_WIN32)

            case '\\':

                break;

#endif

            case '/':

                if (!merge_slashes) {

                    *u++ = ch;

                }

                break;

            case '.':

                state = sw_dot;

                *u++ = ch;

                break;

            case '%':

                quoted_state = state;

                state = sw_quoted;

                break;

            case '?':

                r->args_start = p;

                goto args;

            case '#':

                goto done;

            case '+':

                r->plus_in_uri = 1;

            default:

                state = sw_usual;

                *u++ = ch;

                break;

            }

            ch = *p++;

            break;

        case sw_dot:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                state = sw_usual;

                *u++ = ch;

                ch = *p++;

                break;

            }

            switch (ch) {

#if (NGX_WIN32)

            case '\\':

#endif

            case '/':

                state = sw_slash;

                u--;

                break;

            case '.':

                state = sw_dot_dot;

                *u++ = ch;

                break;

            case '%':

                quoted_state = state;

                state = sw_quoted;

                break;

            case '?':

                r->args_start = p;

                goto args;

            case '#':

                goto done;

            case '+':

                r->plus_in_uri = 1;

            default:

                state = sw_usual;

                *u++ = ch;

                break;

            }

            ch = *p++;

            break;

        case sw_dot_dot:

            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

                state = sw_usual;

                *u++ = ch;

                ch = *p++;

                break;

            }

            switch (ch) {

#if (NGX_WIN32)

            case '\\':

#endif

            case '/':

                state = sw_slash;

                u -= 5;

                for ( ;; ) {

                    if (u < r->uri.data) {

                        return NGX_HTTP_PARSE_INVALID_REQUEST;

                    }

                    if (*u == '/') {

                        u++;

                        break;

                    }

                    u--;

                }

                break;

            case '%':

                quoted_state = state;

                state = sw_quoted;

                break;

            case '?':

                r->args_start = p;

                goto args;

            case '#':

                goto done;

            case '+':

                r->plus_in_uri = 1;

            default:

                state = sw_usual;

                *u++ = ch;

                break;

            }

            ch = *p++;

            break;

        case sw_quoted:

            r->quoted_uri = 1;

            if (ch >= '0' && ch <= '9') {

                decoded = (u_char) (ch - '0');

                state = sw_quoted_second;

                ch = *p++;

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                decoded = (u_char) (c - 'a' + 10);

                state = sw_quoted_second;

                ch = *p++;

                break;

            }

            return NGX_HTTP_PARSE_INVALID_REQUEST;

        case sw_quoted_second:

            if (ch >= '0' && ch <= '9') {

                ch = (u_char) ((decoded << 4) + ch - '0');

                if (ch == '%' || ch == '#') {

                    state = sw_usual;

                    *u++ = ch;

                    ch = *p++;

                    break;

                } else if (ch == '\0') {

                    return NGX_HTTP_PARSE_INVALID_REQUEST;

                }

                state = quoted_state;

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                ch = (u_char) ((decoded << 4) + c - 'a' + 10);

                if (ch == '?') {

                    state = sw_usual;

                    *u++ = ch;

                    ch = *p++;

                    break;

                } else if (ch == '+') {

                    r->plus_in_uri = 1;

                }

                state = quoted_state;

                break;

            }

            return NGX_HTTP_PARSE_INVALID_REQUEST;

        }

    }

done:

    r->uri.len = u - r->uri.data;

    if (r->uri_ext) {

        r->exten.len = u - r->uri_ext;

        r->exten.data = r->uri_ext;

    }

    r->uri_ext = NULL;

    return NGX_OK;

args:

    while (p < r->uri_end) {

        if (*p++ != '#') {

            continue;

        }

        r->args.len = p - 1 - r->args_start;

        r->args.data = r->args_start;

        r->args_start = NULL;

        break;

    }

    r->uri.len = u - r->uri.data;

    if (r->uri_ext) {

        r->exten.len = u - r->uri_ext;

        r->exten.data = r->uri_ext;

    }

    r->uri_ext = NULL;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_http_status_t *status)

{

    u_char   ch;

    u_char  *p;

    enum {

        sw_start = 0,

        sw_H,

        sw_HT,

        sw_HTT,

        sw_HTTP,

        sw_first_major_digit,

        sw_major_digit,

        sw_first_minor_digit,

        sw_minor_digit,

        sw_status,

        sw_space_after_status,

        sw_status_text,

        sw_almost_done

    } state;

    state = r->state;

    for (p = b->pos; p < b->last; p++) {

        ch = *p;

        switch (state) {

        /* "HTTP/" */

        case sw_start:

            switch (ch) {

            case 'H':

                state = sw_H;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_H:

            switch (ch) {

            case 'T':

                state = sw_HT;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HT:

            switch (ch) {

            case 'T':

                state = sw_HTT;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HTT:

            switch (ch) {

            case 'P':

                state = sw_HTTP;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HTTP:

            switch (ch) {

            case '/':

                state = sw_first_major_digit;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        /* the first digit of major HTTP version */

        case sw_first_major_digit:

            if (ch < '1' || ch > '9') {

                return NGX_ERROR;

            }

            r->http_major = ch - '0';

            state = sw_major_digit;

            break;

        /* the major HTTP version or dot */

        case sw_major_digit:

            if (ch == '.') {

                state = sw_first_minor_digit;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            r->http_major = r->http_major * 10 + ch - '0';

            break;

        /* the first digit of minor HTTP version */

        case sw_first_minor_digit:

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            r->http_minor = ch - '0';

            state = sw_minor_digit;

            break;

        /* the minor HTTP version or the end of the request line */

        case sw_minor_digit:

            if (ch == ' ') {

                state = sw_status;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            r->http_minor = r->http_minor * 10 + ch - '0';

            break;

        /* HTTP status code */

        case sw_status:

            if (ch == ' ') {

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            status->code = status->code * 10 + ch - '0';

            if (++status->count == 3) {

                state = sw_space_after_status;

                status->start = p - 2;

            }

            break;

        /* space or end of line */

        case sw_space_after_status:

            switch (ch) {

            case ' ':

                state = sw_status_text;

                break;

            case '.':                    /* IIS may send 403.1, 403.2, etc */

                state = sw_status_text;

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

            break;

        /* any text until end of line */

        case sw_status_text:

            switch (ch) {

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            }

            break;

        /* end of status line */

        case sw_almost_done:

            status->end = p - 1;

            switch (ch) {

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

        }

    }

    b->pos = p;

    r->state = state;

    return NGX_AGAIN;

done:

    b->pos = p + 1;

    if (status->end == NULL) {

        status->end = p;

    }

    status->http_version = r->http_major * 1000 + r->http_minor;

    r->state = sw_start;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,

    ngx_str_t *args, ngx_uint_t *flags)

{

    u_char      ch, *p, *src, *dst;

    size_t      len;

    ngx_uint_t  quoted;

    len = uri->len;

    p = uri->data;

    quoted = 0;

    if (len == 0 || p[0] == '?') {

        goto unsafe;

    }

    if (p[0] == '.' && len > 1 && p[1] == '.'

        && (len == 2 || ngx_path_separator(p[2])))

    {

        goto unsafe;

    }

    for ( /* void */ ; len; len--) {

        ch = *p++;

        if (ch == '%') {

            quoted = 1;

            continue;

        }

        if (usual[ch >> 5] & (1 << (ch & 0x1f))) {

            continue;

        }

        if (ch == '?') {

            args->len = len - 1;

            args->data = p;

            uri->len -= len;

            break;

        }

        if (ch == '\0') {

            goto unsafe;

        }

        if (ngx_path_separator(ch) && len > 2) {

            /* detect "/../" and "/.." */

            if (p[0] == '.' && p[1] == '.'

                && (len == 3 || ngx_path_separator(p[2])))

            {

                goto unsafe;

            }

        }

    }

    if (quoted) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "escaped URI: \"%V\"", uri);

        src = uri->data;

        dst = ngx_pnalloc(r->pool, uri->len);

        if (dst == NULL) {

            return NGX_ERROR;

        }

        uri->data = dst;

        ngx_unescape_uri(&dst, &src, uri->len, 0);

        uri->len = dst - uri->data;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "unescaped URI: \"%V\"", uri);

        len = uri->len;

        p = uri->data;

        if (p[0] == '.' && len > 1 && p[1] == '.'

            && (len == 2 || ngx_path_separator(p[2])))

        {

            goto unsafe;

        }

        for ( /* void */ ; len; len--) {

            ch = *p++;

            if (ch == '\0') {

                goto unsafe;

            }

            if (ngx_path_separator(ch) && len > 2) {

                /* detect "/../" and "/.." */

                if (p[0] == '.' && p[1] == '.'

                    && (len == 3 || ngx_path_separator(p[2])))

                {

                    goto unsafe;

                }

            }

        }

    }

    return NGX_OK;

unsafe:

    if (*flags & NGX_HTTP_LOG_UNSAFE) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "unsafe URI \"%V\" was detected", uri);

    }

    return NGX_ERROR;

}

ngx_int_t

‌ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,

    ngx_str_t *value)

{

    ngx_uint_t         i;

    u_char            *start, *last, *end, ch;

    ngx_table_elt_t  **h;

    h = headers->elts;

    for (i = 0; i < headers->nelts; i++) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,

                       "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);

        if (name->len > h[i]->value.len) {

            continue;

        }

        start = h[i]->value.data;

        end = h[i]->value.data + h[i]->value.len;

        while (start < end) {

            if (ngx_strncasecmp(start, name->data, name->len) != 0) {

                goto skip;

            }

            for (start += name->len; start < end && *start == ' '; start++) {

                /* void */

            }

            if (value == NULL) {

                if (start == end || *start == ',') {

                    return i;

                }

                goto skip;

            }

            if (start == end || *start++ != '=') {

                /* the invalid header value */

                goto skip;

            }

            while (start < end && *start == ' ') { start++; }

            for (last = start; last < end && *last != ';'; last++) {

                /* void */

            }

            value->len = last - start;

            value->data = start;

            return i;

        skip:

            while (start < end) {

                ch = *start++;

                if (ch == ';' || ch == ',') {

                    break;

                }

            }

            while (start < end && *start == ' ') { start++; }

        }

    }

    return NGX_DECLINED;

}

ngx_int_t

‌ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,

    ngx_str_t *value)

{

    ngx_uint_t         i;

    u_char            *start, *last, *end;

    ngx_table_elt_t  **h;

    h = headers->elts;

    for (i = 0; i < headers->nelts; i++) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,

                       "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);

        if (name->len >= h[i]->value.len) {

            continue;

        }

        start = h[i]->value.data;

        end = h[i]->value.data + h[i]->value.len;

        if (ngx_strncasecmp(start, name->data, name->len) != 0) {

            continue;

        }

        for (start += name->len; start < end && *start == ' '; start++) {

            /* void */

        }

        if (start == end || *start++ != '=') {

            /* the invalid header value */

            continue;

        }

        while (start < end && *start == ' ') { start++; }

        for (last = start; last < end && *last != ';'; last++) {

            /* void */

        }

        value->len = last - start;

        value->data = start;

        return i;

    }

    return NGX_DECLINED;

}

ngx_int_t

‌ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)

{

    u_char  *p, *last;

    if (r->args.len == 0) {

        return NGX_DECLINED;

    }

    p = r->args.data;

    last = p + r->args.len;

    for ( /* void */ ; p < last; p++) {

        /* we need '=' after name, so drop one char from last */

        p = ngx_strlcasestrn(p, last - 1, name, len - 1);

        if (p == NULL) {

            return NGX_DECLINED;

        }

        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {

            value->data = p + len + 1;

            p = ngx_strlchr(p, last, '&');

            if (p == NULL) {

                p = r->args.data + r->args.len;

            }

            value->len = p - value->data;

            return NGX_OK;

        }

    }

    return NGX_DECLINED;

}

void

‌ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)

{

    u_char  *p, *last;

    last = uri->data + uri->len;

    p = ngx_strlchr(uri->data, last, '?');

    if (p) {

        uri->len = p - uri->data;

        p++;

        args->len = last - p;

        args->data = p;

    } else {

        args->len = 0;

    }

}

ngx_int_t

‌ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,

    ngx_http_chunked_t *ctx)

{

    u_char     *pos, ch, c;

    ngx_int_t   rc;

    enum {

        sw_chunk_start = 0,

        sw_chunk_size,

        sw_chunk_extension,

        sw_chunk_extension_almost_done,

        sw_chunk_data,

        sw_after_data,

        sw_after_data_almost_done,

        sw_last_chunk_extension,

        sw_last_chunk_extension_almost_done,

        sw_trailer,

        sw_trailer_almost_done,

        sw_trailer_header,

        sw_trailer_header_almost_done

    } state;

    state = ctx->state;

    if (state == sw_chunk_data && ctx->size == 0) {

        state = sw_after_data;

    }

    rc = NGX_AGAIN;

    for (pos = b->pos; pos < b->last; pos++) {

        ch = *pos;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http chunked byte: %02Xd s:%d", ch, state);

        switch (state) {

        case sw_chunk_start:

            if (ch >= '0' && ch <= '9') {

                state = sw_chunk_size;

                ctx->size = ch - '0';

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                state = sw_chunk_size;

                ctx->size = c - 'a' + 10;

                break;

            }

            goto invalid;

        case sw_chunk_size:

            if (ch >= '0' && ch <= '9') {

                ctx->size = ctx->size * 16 + (ch - '0');

                break;

            }

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'f') {

                ctx->size = ctx->size * 16 + (c - 'a' + 10);

                break;

            }

            if (ctx->size == 0) {

                switch (ch) {

                case CR:

                    state = sw_last_chunk_extension_almost_done;

                    break;

                case LF:

                    state = sw_trailer;

                    break;

                case ';':

                case ' ':

                case '\t':

                    state = sw_last_chunk_extension;

                    break;

                default:

                    goto invalid;

                }

                break;

            }

            switch (ch) {

            case CR:

                state = sw_chunk_extension_almost_done;

                break;

            case LF:

                state = sw_chunk_data;

                break;

            case ';':

            case ' ':

            case '\t':

                state = sw_chunk_extension;

                break;

            default:

                goto invalid;

            }

            break;

        case sw_chunk_extension:

            switch (ch) {

            case CR:

                state = sw_chunk_extension_almost_done;

                break;

            case LF:

                state = sw_chunk_data;

            }

            break;

        case sw_chunk_extension_almost_done:

            if (ch == LF) {

                state = sw_chunk_data;

                break;

            }

            goto invalid;

        case sw_chunk_data:

            rc = NGX_OK;

            goto data;

        case sw_after_data:

            switch (ch) {

            case CR:

                state = sw_after_data_almost_done;

                break;

            case LF:

                state = sw_chunk_start;

            }

            break;

        case sw_after_data_almost_done:

            if (ch == LF) {

                state = sw_chunk_start;

                break;

            }

            goto invalid;

        case sw_last_chunk_extension:

            switch (ch) {

            case CR:

                state = sw_last_chunk_extension_almost_done;

                break;

            case LF:

                state = sw_trailer;

            }

            break;

        case sw_last_chunk_extension_almost_done:

            if (ch == LF) {

                state = sw_trailer;

                break;

            }

            goto invalid;

        case sw_trailer:

            switch (ch) {

            case CR:

                state = sw_trailer_almost_done;

                break;

            case LF:

                goto done;

            default:

                state = sw_trailer_header;

            }

            break;

        case sw_trailer_almost_done:

            if (ch == LF) {

                goto done;

            }

            goto invalid;

        case sw_trailer_header:

            switch (ch) {

            case CR:

                state = sw_trailer_header_almost_done;

                break;

            case LF:

                state = sw_trailer;

            }

            break;

        case sw_trailer_header_almost_done:

            if (ch == LF) {

                state = sw_trailer;

                break;

            }

            goto invalid;

        }

    }

data:

    ctx->state = state;

    b->pos = pos;

    switch (state) {

    case sw_chunk_start:

        ctx->length = 3 /* "0" LF LF */;

        break;

    case sw_chunk_size:

        ctx->length = 1 /* LF */

                      + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */

                                   : 1 /* LF */);

        break;

    case sw_chunk_extension:

    case sw_chunk_extension_almost_done:

        ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;

        break;

    case sw_chunk_data:

        ctx->length = ctx->size + 4 /* LF "0" LF LF */;

        break;

    case sw_after_data:

    case sw_after_data_almost_done:

        ctx->length = 4 /* LF "0" LF LF */;

        break;

    case sw_last_chunk_extension:

    case sw_last_chunk_extension_almost_done:

        ctx->length = 2 /* LF LF */;

        break;

    case sw_trailer:

    case sw_trailer_almost_done:

        ctx->length = 1 /* LF */;

        break;

    case sw_trailer_header:

    case sw_trailer_header_almost_done:

        ctx->length = 2 /* LF LF */;

        break;

    }

    if (ctx->size < 0 || ctx->length < 0) {

        goto invalid;

    }

    return rc;

done:

    ctx->state = 0;

    b->pos = pos + 1;

    return NGX_DONE;

invalid:

    return NGX_ERROR;

}

src/http/ngx_http_spdy.c - nginx-1.7.9

Global variables defined

ngx_http_spdy_dict

ngx_http_spdy_request_headers

Data types defined

ngx_http_spdy_request_header_t

Functions defined

ngx_http_spdy_adjust_windows

ngx_http_spdy_alloc_large_header_buffer

ngx_http_spdy_close_stream

ngx_http_spdy_close_stream_handler

ngx_http_spdy_construct_request_line

ngx_http_spdy_create_stream

ngx_http_spdy_ctl_frame_handler

ngx_http_spdy_finalize_connection

ngx_http_spdy_get_ctl_frame

ngx_http_spdy_get_stream_by_id

ngx_http_spdy_handle_connection

ngx_http_spdy_handle_connection_handler

ngx_http_spdy_handle_request_header

ngx_http_spdy_init

ngx_http_spdy_init_request_body

ngx_http_spdy_keepalive_handler

ngx_http_spdy_parse_header

ngx_http_spdy_parse_host

ngx_http_spdy_parse_method

ngx_http_spdy_parse_path

ngx_http_spdy_parse_scheme

ngx_http_spdy_parse_version

ngx_http_spdy_pool_cleanup

ngx_http_spdy_proxy_protocol

ngx_http_spdy_read_handler

ngx_http_spdy_read_request_body

ngx_http_spdy_request_headers_init

ngx_http_spdy_run_request

ngx_http_spdy_send_output_queue

ngx_http_spdy_send_rst_stream

ngx_http_spdy_send_settings

ngx_http_spdy_send_window_update

ngx_http_spdy_settings_frame_handler

ngx_http_spdy_state_complete

ngx_http_spdy_state_data

ngx_http_spdy_state_head

ngx_http_spdy_state_headers

ngx_http_spdy_state_headers_error

ngx_http_spdy_state_headers_skip

ngx_http_spdy_state_inflate_error

ngx_http_spdy_state_internal_error

ngx_http_spdy_state_ping

ngx_http_spdy_state_protocol_error

ngx_http_spdy_state_read_data

ngx_http_spdy_state_rst_stream

ngx_http_spdy_state_save

ngx_http_spdy_state_settings

ngx_http_spdy_state_skip

ngx_http_spdy_state_syn_stream

ngx_http_spdy_state_window_update

ngx_http_spdy_terminate_stream

ngx_http_spdy_write_handler

ngx_http_spdy_zalloc

ngx_http_spdy_zfree

Macros defined

NGX_SPDY_CANCEL

NGX_SPDY_CONNECTION_WINDOW

NGX_SPDY_CTL_FRAME_BUFFER_SIZE

NGX_SPDY_FLOW_CONTROL_ERROR

NGX_SPDY_FRAME_TOO_LARGE

NGX_SPDY_INIT_STREAM_WINDOW

NGX_SPDY_INTERNAL_ERROR

NGX_SPDY_INVALID_STREAM

NGX_SPDY_MAX_WINDOW

NGX_SPDY_PROTOCOL_ERROR

NGX_SPDY_REFUSED_STREAM

NGX_SPDY_REQUEST_HEADERS

NGX_SPDY_SETTINGS_FLAG_PERSIST

NGX_SPDY_SETTINGS_FLAG_PERSISTED

NGX_SPDY_SETTINGS_INIT_WINDOW

NGX_SPDY_SETTINGS_MAX_STREAMS

NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE

NGX_SPDY_STREAM_ALREADY_CLOSED

NGX_SPDY_STREAM_IN_USE

NGX_SPDY_STREAM_WINDOW

NGX_SPDY_UNSUPPORTED_VERSION

ngx_http_spdy_stream_index

ngx_http_spdy_streams_index_size

ngx_spdy_ctl_frame_check

ngx_spdy_ctl_frame_type

ngx_spdy_data_frame_check

ngx_spdy_frame_flags

ngx_spdy_frame_id

ngx_spdy_frame_length

ngx_spdy_frame_parse_delta

ngx_spdy_frame_parse_sid

ngx_spdy_frame_parse_uint16

ngx_spdy_frame_parse_uint16

ngx_spdy_frame_parse_uint32

ngx_spdy_frame_parse_uint32

ngx_str5cmp

ngx_str5cmp

Source code

/*

* Copyright (C) Nginx, Inc.

* Copyright (C) Valentin V. Bartenev

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_http_spdy_module.h>

#include <zlib.h>

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

‌#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \

    *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0)                   \

        && m[4] == c4

#else

‌#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \

    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4

#endif

#if (NGX_HAVE_NONALIGNED)

‌#define ngx_spdy_frame_parse_uint16(p)  ntohs(*(uint16_t *) (p))

‌#define ngx_spdy_frame_parse_uint32(p)  ntohl(*(uint32_t *) (p))

#else

‌#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1])

‌#define ngx_spdy_frame_parse_uint32(p)                                        \

    ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])

#endif

‌#define ngx_spdy_frame_parse_sid(p)                                           \

    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)

‌#define ngx_spdy_frame_parse_delta(p)                                         \

    (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)

‌#define ngx_spdy_ctl_frame_check(h)                                           \

    (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0))

‌#define ngx_spdy_data_frame_check(h)                                          \

    (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31))

‌#define ngx_spdy_ctl_frame_type(h)   ((h) & 0x0000ffff)

‌#define ngx_spdy_frame_flags(p)      ((p) >> 24)

‌#define ngx_spdy_frame_length(p)     ((p) & 0x00ffffff)

‌#define ngx_spdy_frame_id(p)         ((p) & 0x00ffffff)

‌#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE  4096

‌#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE     16

‌#define NGX_SPDY_PROTOCOL_ERROR            1

‌#define NGX_SPDY_INVALID_STREAM            2

‌#define NGX_SPDY_REFUSED_STREAM            3

‌#define NGX_SPDY_UNSUPPORTED_VERSION       4

‌#define NGX_SPDY_CANCEL                    5

‌#define NGX_SPDY_INTERNAL_ERROR            6

‌#define NGX_SPDY_FLOW_CONTROL_ERROR        7

‌#define NGX_SPDY_STREAM_IN_USE             8

‌#define NGX_SPDY_STREAM_ALREADY_CLOSED     9

/* deprecated                              10 */

‌#define NGX_SPDY_FRAME_TOO_LARGE           11

‌#define NGX_SPDY_SETTINGS_MAX_STREAMS      4

‌#define NGX_SPDY_SETTINGS_INIT_WINDOW      7

‌#define NGX_SPDY_SETTINGS_FLAG_PERSIST     0x01

‌#define NGX_SPDY_SETTINGS_FLAG_PERSISTED   0x02

‌#define NGX_SPDY_MAX_WINDOW                NGX_MAX_INT32_VALUE

‌#define NGX_SPDY_CONNECTION_WINDOW         65536

‌#define NGX_SPDY_INIT_STREAM_WINDOW        65536

‌#define NGX_SPDY_STREAM_WINDOW             NGX_SPDY_MAX_WINDOW

typedef struct {

    ngx_uint_t    hash;

    u_char        len;

    u_char        header[7];

    ngx_int_t   (*handler)(ngx_http_request_t *r);

‌} ngx_http_spdy_request_header_t;

static void ngx_http_spdy_read_handler(ngx_event_t *rev);

static void ngx_http_spdy_write_handler(ngx_event_t *wev);

static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);

static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler);

static u_char *ngx_http_spdy_state_inflate_error(

    ngx_http_spdy_connection_t *sc, int rc);

static u_char *ngx_http_spdy_state_protocol_error(

    ngx_http_spdy_connection_t *sc);

static u_char *ngx_http_spdy_state_internal_error(

    ngx_http_spdy_connection_t *sc);

static ngx_int_t ngx_http_spdy_send_window_update(

    ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ngx_uint_t delta);

static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc,

    ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority);

static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc);

static ngx_int_t ngx_http_spdy_settings_frame_handler(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);

static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame(

    ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority);

static ngx_int_t ngx_http_spdy_ctl_frame_handler(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);

static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream(

    ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority);

static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id(

    ngx_http_spdy_connection_t *sc, ngx_uint_t sid);

‌#define ngx_http_spdy_streams_index_size(sscf)  (sscf->streams_index_mask + 1)

‌#define ngx_http_spdy_stream_index(sscf, sid)                                 \

    ((sid >> 1) & sscf->streams_index_mask)

static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_parse_host(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_parse_path(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r);

static void ngx_http_spdy_run_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r);

static ngx_int_t ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream, ngx_uint_t status);

static void ngx_http_spdy_close_stream_handler(ngx_event_t *ev);

static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev);

static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev);

static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,

    ngx_int_t rc);

static ngx_int_t ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc,

    ssize_t delta);

static void ngx_http_spdy_pool_cleanup(void *data);

static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size);

static void ngx_http_spdy_zfree(void *opaque, void *address);

‌static const u_char ngx_http_spdy_dict[] = {

    0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   /* - - - - o p t i */

    0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   /* o n s - - - - h */

    0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   /* e a d - - - - p */

    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   /* o s t - - - - p */

    0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   /* u t - - - - d e */

    0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   /* l e t e - - - - */

    0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   /* t r a c e - - - */

    0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   /* - a c c e p t - */

    0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */

    0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* t - c h a r s e */

    0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   /* t - - - - a c c */

    0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e p t - e n c o */

    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   /* d i n g - - - - */

    0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   /* a c c e p t - l */

    0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   /* a n g u a g e - */

    0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   /* - - - a c c e p */

    0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   /* t - r a n g e s */

    0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   /* - - - - a g e - */

    0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   /* - - - a l l o w */

    0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   /* - - - - a u t h */

    0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   /* o r i z a t i o */

    0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   /* n - - - - c a c */

    0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   /* h e - c o n t r */

    0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   /* o l - - - - c o */

    0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   /* n n e c t i o n */

    0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */

    0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   /* e n t - b a s e */

    0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */

    0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /* e n t - e n c o */

    0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   /* d i n g - - - - */

    0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   /* c o n t e n t - */

    0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   /* l a n g u a g e */

    0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   /* - - - - c o n t */

    0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   /* e n t - l e n g */

    0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   /* t h - - - - c o */

    0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   /* n t e n t - l o */

    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* c a t i o n - - */

    0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */

    0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   /* t - m d 5 - - - */

    0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   /* - c o n t e n t */

    0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   /* - r a n g e - - */

    0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /* - - c o n t e n */

    0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   /* t - t y p e - - */

    0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   /* - - d a t e - - */

    0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   /* - - e t a g - - */

    0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   /* - - e x p e c t */

    0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   /* - - - - e x p i */

    0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   /* r e s - - - - f */

    0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   /* r o m - - - - h */

    0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   /* o s t - - - - i */

    0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   /* f - m a t c h - */

    0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   /* - - - i f - m o */

    0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   /* d i f i e d - s */

    0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   /* i n c e - - - - */

    0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   /* i f - n o n e - */

    0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   /* m a t c h - - - */

    0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   /* - i f - r a n g */

    0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   /* e - - - - i f - */

    0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   /* u n m o d i f i */

    0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   /* e d - s i n c e */

    0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   /* - - - - l a s t */

    0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   /* - m o d i f i e */

    0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   /* d - - - - l o c */

    0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   /* a t i o n - - - */

    0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   /* - m a x - f o r */

    0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   /* w a r d s - - - */

    0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   /* - p r a g m a - */

    0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   /* - - - p r o x y */

    0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   /* - a u t h e n t */

    0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   /* i c a t e - - - */

    0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   /* - p r o x y - a */

    0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   /* u t h o r i z a */

    0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   /* t i o n - - - - */

    0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   /* r a n g e - - - */

    0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   /* - r e f e r e r */

    0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   /* - - - - r e t r */

    0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   /* y - a f t e r - */

    0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   /* - - - s e r v e */

    0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   /* r - - - - t e - */

    0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   /* - - - t r a i l */

    0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   /* e r - - - - t r */

    0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   /* a n s f e r - e */

    0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   /* n c o d i n g - */

    0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   /* - - - u p g r a */

    0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   /* d e - - - - u s */

    0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   /* e r - a g e n t */

    0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   /* - - - - v a r y */

    0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   /* - - - - v i a - */

    0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   /* - - - w a r n i */

    0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   /* n g - - - - w w */

    0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   /* w - a u t h e n */

    0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   /* t i c a t e - - */

    0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   /* - - m e t h o d */

    0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   /* - - - - g e t - */

    0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   /* - - - s t a t u */

    0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   /* s - - - - 2 0 0 */

    0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   /* - O K - - - - v */

    0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /* e r s i o n - - */

    0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   /* - - H T T P - 1 */

    0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   /* - 1 - - - - u r */

    0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   /* l - - - - p u b */

    0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   /* l i c - - - - s */

    0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   /* e t - c o o k i */

    0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   /* e - - - - k e e */

    0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   /* p - a l i v e - */

    0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   /* - - - o r i g i */

    0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   /* n 1 0 0 1 0 1 2 */

    0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   /* 0 1 2 0 2 2 0 5 */

    0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   /* 2 0 6 3 0 0 3 0 */

    0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   /* 2 3 0 3 3 0 4 3 */

    0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   /* 0 5 3 0 6 3 0 7 */

    0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   /* 4 0 2 4 0 5 4 0 */

    0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   /* 6 4 0 7 4 0 8 4 */

    0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   /* 0 9 4 1 0 4 1 1 */

    0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   /* 4 1 2 4 1 3 4 1 */

    0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   /* 4 4 1 5 4 1 6 4 */

    0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   /* 1 7 5 0 2 5 0 4 */

    0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   /* 5 0 5 2 0 3 - N */

    0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   /* o n - A u t h o */

    0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   /* r i t a t i v e */

    0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   /* - I n f o r m a */

    0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   /* t i o n 2 0 4 - */

    0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   /* N o - C o n t e */

    0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   /* n t 3 0 1 - M o */

    0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   /* v e d - P e r m */

    0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   /* a n e n t l y 4 */

    0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   /* 0 0 - B a d - R */

    0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   /* e q u e s t 4 0 */

    0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   /* 1 - U n a u t h */

    0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   /* o r i z e d 4 0 */

    0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   /* 3 - F o r b i d */

    0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   /* d e n 4 0 4 - N */

    0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   /* o t - F o u n d */

    0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   /* 5 0 0 - I n t e */

    0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   /* r n a l - S e r */

    0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   /* v e r - E r r o */

    0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   /* r 5 0 1 - N o t */

    0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   /* - I m p l e m e */

    0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   /* n t e d 5 0 3 - */

    0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   /* S e r v i c e - */

    0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   /* U n a v a i l a */

    0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   /* b l e J a n - F */

    0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   /* e b - M a r - A */

    0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   /* p r - M a y - J */

    0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   /* u n - J u l - A */

    0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   /* u g - S e p t - */

    0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   /* O c t - N o v - */

    0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   /* D e c - 0 0 - 0 */

    0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   /* 0 - 0 0 - M o n */

    0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   /* - - T u e - - W */

    0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   /* e d - - T h u - */

    0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   /* - F r i - - S a */

    0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   /* t - - S u n - - */

    0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   /* G M T c h u n k */

    0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   /* e d - t e x t - */

    0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   /* h t m l - i m a */

    0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   /* g e - p n g - i */

    0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   /* m a g e - j p g */

    0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   /* - i m a g e - g */

    0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* i f - a p p l i */

    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */

    0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /* m l - a p p l i */

    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /* c a t i o n - x */

    0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   /* h t m l - x m l */

    0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   /* - t e x t - p l */

    0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   /* a i n - t e x t */

    0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   /* - j a v a s c r */

    0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   /* i p t - p u b l */

    0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   /* i c p r i v a t */

    0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   /* e m a x - a g e */

    0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   /* - g z i p - d e */

    0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   /* f l a t e - s d */

    0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /* c h c h a r s e */

    0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   /* t - u t f - 8 c */

    0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   /* h a r s e t - i */

    0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   /* s o - 8 8 5 9 - */

    0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   /* 1 - u t f - - - */

    0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          /* - e n q - 0 -   */

};

‌static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {

    { 0, 6, "method", ngx_http_spdy_parse_method },

    { 0, 6, "scheme", ngx_http_spdy_parse_scheme },

    { 0, 4, "host", ngx_http_spdy_parse_host },

    { 0, 4, "path", ngx_http_spdy_parse_path },

    { 0, 7, "version", ngx_http_spdy_parse_version },

};

‌#define NGX_SPDY_REQUEST_HEADERS                                              \

    (sizeof(ngx_http_spdy_request_headers)                                    \

     / sizeof(ngx_http_spdy_request_header_t))

void

‌ngx_http_spdy_init(ngx_event_t *rev)

{

    int                          rc;

    ngx_connection_t            *c;

    ngx_pool_cleanup_t          *cln;

    ngx_http_connection_t       *hc;

    ngx_http_spdy_srv_conf_t    *sscf;

    ngx_http_spdy_main_conf_t   *smcf;

    ngx_http_spdy_connection_t  *sc;

    c = rev->data;

    hc = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init spdy request");

    c->log->action = "processing SPDY";

    smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module);

    if (smcf->recv_buffer == NULL) {

        smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size);

        if (smcf->recv_buffer == NULL) {

            ngx_http_close_connection(c);

            return;

        }

    }

    sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t));

    if (sc == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    sc->connection = c;

    sc->http_connection = hc;

    sc->send_window = NGX_SPDY_CONNECTION_WINDOW;

    sc->recv_window = NGX_SPDY_CONNECTION_WINDOW;

    sc->init_window = NGX_SPDY_INIT_STREAM_WINDOW;

    sc->handler = hc->proxy_protocol ? ngx_http_spdy_proxy_protocol

                                     : ngx_http_spdy_state_head;

    sc->zstream_in.zalloc = ngx_http_spdy_zalloc;

    sc->zstream_in.zfree = ngx_http_spdy_zfree;

    sc->zstream_in.opaque = sc;

    rc = inflateInit(&sc->zstream_in);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "inflateInit() failed: %d", rc);

        ngx_http_close_connection(c);

        return;

    }

    sc->zstream_out.zalloc = ngx_http_spdy_zalloc;

    sc->zstream_out.zfree = ngx_http_spdy_zfree;

    sc->zstream_out.opaque = sc;

    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module);

    rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp,

                      Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "deflateInit2() failed: %d", rc);

        ngx_http_close_connection(c);

        return;

    }

    rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict,

                              sizeof(ngx_http_spdy_dict));

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "deflateSetDictionary() failed: %d", rc);

        ngx_http_close_connection(c);

        return;

    }

    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);

    if (sc->pool == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));

    if (cln == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    cln->handler = ngx_http_spdy_pool_cleanup;

    cln->data = sc;

    sc->streams_index = ngx_pcalloc(sc->pool,

                                    ngx_http_spdy_streams_index_size(sscf)

                                    * sizeof(ngx_http_spdy_stream_t *));

    if (sc->streams_index == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) {

        ngx_http_close_connection(c);

        return;

    }

    if (ngx_http_spdy_send_window_update(sc, 0, NGX_SPDY_MAX_WINDOW

                                                - sc->recv_window)

        == NGX_ERROR)

    {

        ngx_http_close_connection(c);

        return;

    }

    sc->recv_window = NGX_SPDY_MAX_WINDOW;

    ngx_queue_init(&sc->waiting);

    ngx_queue_init(&sc->posted);

    c->data = sc;

    rev->handler = ngx_http_spdy_read_handler;

    c->write->handler = ngx_http_spdy_write_handler;

    ngx_http_spdy_read_handler(rev);

}

static void

‌ngx_http_spdy_read_handler(ngx_event_t *rev)

{

    u_char                      *p, *end;

    size_t                       available;

    ssize_t                      n;

    ngx_connection_t            *c;

    ngx_http_spdy_main_conf_t   *smcf;

    ngx_http_spdy_connection_t  *sc;

    c = rev->data;

    sc = c->data;

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler");

    sc->blocked = 1;

    smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx,

                                         ngx_http_spdy_module);

    available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE;

    do {

        p = smcf->recv_buffer;

        ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE);

        end = p + sc->buffer_used;

        n = c->recv(c, end, available);

        if (n == NGX_AGAIN) {

            break;

        }

        if (n == 0 && (sc->incomplete || sc->processing)) {

            ngx_log_error(NGX_LOG_INFO, c->log, 0,

                          "client prematurely closed connection");

        }

        if (n == 0 || n == NGX_ERROR) {

            ngx_http_spdy_finalize_connection(sc,

                                              NGX_HTTP_CLIENT_CLOSED_REQUEST);

            return;

        }

        end += n;

        sc->buffer_used = 0;

        sc->incomplete = 0;

        do {

            p = sc->handler(sc, p, end);

            if (p == NULL) {

                return;

            }

        } while (p != end);

    } while (rev->ready);

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) {

        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);

        return;

    }

    sc->blocked = 0;

    if (sc->processing) {

        if (rev->timer_set) {

            ngx_del_timer(rev);

        }

        return;

    }

    ngx_http_spdy_handle_connection(sc);

}

static void

‌ngx_http_spdy_write_handler(ngx_event_t *wev)

{

    ngx_int_t                    rc;

    ngx_queue_t                 *q;

    ngx_connection_t            *c;

    ngx_http_spdy_stream_t      *stream;

    ngx_http_spdy_connection_t  *sc;

    c = wev->data;

    sc = c->data;

    if (wev->timedout) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "spdy write event timed out");

        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler");

    sc->blocked = 1;

    rc = ngx_http_spdy_send_output_queue(sc);

    if (rc == NGX_ERROR) {

        ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);

        return;

    }

    while (!ngx_queue_empty(&sc->posted)) {

        q = ngx_queue_head(&sc->posted);

        ngx_queue_remove(q);

        stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);

        stream->handled = 0;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "run spdy stream %ui", stream->id);

        wev = stream->request->connection->write;

        wev->handler(wev);

    }

    sc->blocked = 0;

    if (rc == NGX_AGAIN) {

        return;

    }

    ngx_http_spdy_handle_connection(sc);

}

ngx_int_t

‌ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc)

{

    ngx_chain_t                *cl;

    ngx_event_t                *wev;

    ngx_connection_t           *c;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_spdy_out_frame_t  *out, *frame, *fn;

    c = sc->connection;

    if (c->error) {

        return NGX_ERROR;

    }

    wev = c->write;

    if (!wev->ready) {

        return NGX_OK;

    }

    cl = NULL;

    out = NULL;

    for (frame = sc->last_out; frame; frame = fn) {

        frame->last->next = cl;

        cl = frame->first;

        fn = frame->next;

        frame->next = out;

        out = frame;

        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz",

                       out, out->stream ? out->stream->id : 0, out->priority,

                       out->blocked, out->length);

    }

    cl = c->send_chain(c, cl, 0);

    if (cl == NGX_CHAIN_ERROR) {

        c->error = 1;

        if (!sc->blocked) {

            ngx_post_event(wev, &ngx_posted_events);

        }

        return NGX_ERROR;

    }

    clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx,

                                        ngx_http_core_module);

    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {

        return NGX_ERROR; /* FIXME */

    }

    if (cl) {

        ngx_add_timer(wev, clcf->send_timeout);

    } else {

        if (wev->timer_set) {

            ngx_del_timer(wev);

        }

    }

    for ( /* void */ ; out; out = fn) {

        fn = out->next;

        if (out->handler(sc, out) != NGX_OK) {

            out->blocked = 1;

            out->priority = NGX_SPDY_HIGHEST_PRIORITY;

            break;

        }

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "spdy frame sent: %p sid:%ui bl:%d len:%uz",

                       out, out->stream ? out->stream->id : 0,

                       out->blocked, out->length);

    }

    frame = NULL;

    for ( /* void */ ; out; out = fn) {

        fn = out->next;

        out->next = frame;

        frame = out;

    }

    sc->last_out = frame;

    return NGX_OK;

}

static void

‌ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc)

{

    ngx_connection_t          *c;

    ngx_http_spdy_srv_conf_t  *sscf;

    if (sc->last_out || sc->processing) {

        return;

    }

    c = sc->connection;

    if (c->error) {

        ngx_http_close_connection(c);

        return;

    }

    if (c->buffered) {

        return;

    }

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    if (sc->incomplete) {

        ngx_add_timer(c->read, sscf->recv_timeout);

        return;

    }

    if (ngx_terminate || ngx_exiting) {

        ngx_http_close_connection(c);

        return;

    }

    ngx_destroy_pool(sc->pool);

    sc->pool = NULL;

    sc->free_ctl_frames = NULL;

    sc->free_fake_connections = NULL;

#if (NGX_HTTP_SSL)

    if (c->ssl) {

        ngx_ssl_free_buffer(c);

    }

#endif

    c->destroyed = 1;

    c->idle = 1;

    ngx_reusable_connection(c, 1);

    c->write->handler = ngx_http_empty_handler;

    c->read->handler = ngx_http_spdy_keepalive_handler;

    if (c->write->timer_set) {

        ngx_del_timer(c->write);

    }

    ngx_add_timer(c->read, sscf->keepalive_timeout);

}

static u_char *

‌ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_log_t  *log;

    log = sc->connection->log;

    log->action = "reading PROXY protocol";

    pos = ngx_proxy_protocol_parse(sc->connection, pos, end);

    log->action = "processing SPDY";

    if (pos == NULL) {

        return ngx_http_spdy_state_protocol_error(sc);

    }

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    uint32_t    head, flen;

    ngx_uint_t  type;

    if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_head);

    }

    head = ngx_spdy_frame_parse_uint32(pos);

    pos += sizeof(uint32_t);

    flen = ngx_spdy_frame_parse_uint32(pos);

    sc->flags = ngx_spdy_frame_flags(flen);

    sc->length = ngx_spdy_frame_length(flen);

    pos += sizeof(uint32_t);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "process spdy frame head:%08XD f:%Xd l:%uz",

                   head, sc->flags, sc->length);

    if (ngx_spdy_ctl_frame_check(head)) {

        type = ngx_spdy_ctl_frame_type(head);

        switch (type) {

        case NGX_SPDY_SYN_STREAM:

            return ngx_http_spdy_state_syn_stream(sc, pos, end);

        case NGX_SPDY_SYN_REPLY:

            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                          "client sent unexpected SYN_REPLY frame");

            return ngx_http_spdy_state_protocol_error(sc);

        case NGX_SPDY_RST_STREAM:

            return ngx_http_spdy_state_rst_stream(sc, pos, end);

        case NGX_SPDY_SETTINGS:

            return ngx_http_spdy_state_settings(sc, pos, end);

        case NGX_SPDY_PING:

            return ngx_http_spdy_state_ping(sc, pos, end);

        case NGX_SPDY_GOAWAY:

            return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */

        case NGX_SPDY_HEADERS:

            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                          "client sent unexpected HEADERS frame");

            return ngx_http_spdy_state_protocol_error(sc);

        case NGX_SPDY_WINDOW_UPDATE:

            return ngx_http_spdy_state_window_update(sc, pos, end);

        default:

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                           "spdy control frame with unknown type %ui", type);

            return ngx_http_spdy_state_skip(sc, pos, end);

        }

    }

    if (ngx_spdy_data_frame_check(head)) {

        sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);

        return ngx_http_spdy_state_data(sc, pos, end);

    }

    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                  "client sent invalid frame");

    return ngx_http_spdy_state_protocol_error(sc);

}

static u_char *

‌ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_uint_t                 sid, prio;

    ngx_http_spdy_stream_t    *stream;

    ngx_http_spdy_srv_conf_t  *sscf;

    if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_syn_stream);

    }

    if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent SYN_STREAM frame with incorrect length %uz",

                      sc->length);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    sc->length -= NGX_SPDY_SYN_STREAM_SIZE;

    sid = ngx_spdy_frame_parse_sid(pos);

    prio = pos[8] >> 5;

    pos += NGX_SPDY_SYN_STREAM_SIZE;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio);

    if (sid % 2 == 0 || sid <= sc->last_sid) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent SYN_STREAM frame "

                      "with invalid Stream-ID %ui", sid);

        stream = ngx_http_spdy_get_stream_by_id(sc, sid);

        if (stream) {

            if (ngx_http_spdy_terminate_stream(sc, stream,

                                               NGX_SPDY_PROTOCOL_ERROR)

                != NGX_OK)

            {

                return ngx_http_spdy_state_internal_error(sc);

            }

        }

        return ngx_http_spdy_state_protocol_error(sc);

    }

    sc->last_sid = sid;

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    if (sc->processing >= sscf->concurrent_streams) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "concurrent streams exceeded %ui", sc->processing);

        if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM,

                                          prio)

            != NGX_OK)

        {

            return ngx_http_spdy_state_internal_error(sc);

        }

        return ngx_http_spdy_state_headers_skip(sc, pos, end);

    }

    stream = ngx_http_spdy_create_stream(sc, sid, prio);

    if (stream == NULL) {

        return ngx_http_spdy_state_internal_error(sc);

    }

    stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0;

    stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE

                                      + NGX_SPDY_SYN_STREAM_SIZE

                                      + sc->length;

    sc->stream = stream;

    return ngx_http_spdy_state_headers(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    int                  z;

    size_t               size;

    ngx_buf_t           *buf;

    ngx_int_t            rc;

    ngx_http_request_t  *r;

    size = end - pos;

    if (size == 0) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_headers);

    }

    if (size > sc->length) {

        size = sc->length;

    }

    r = sc->stream->request;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "process spdy header block %uz of %uz", size, sc->length);

    buf = r->header_in;

    sc->zstream_in.next_in = pos;

    sc->zstream_in.avail_in = size;

    sc->zstream_in.next_out = buf->last;

    /* one byte is reserved for null-termination of the last header value */

    sc->zstream_in.avail_out = buf->end - buf->last - 1;

    z = inflate(&sc->zstream_in, Z_NO_FLUSH);

    if (z == Z_NEED_DICT) {

        z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict,

                                 sizeof(ngx_http_spdy_dict));

        if (z != Z_OK) {

            if (z == Z_DATA_ERROR) {

                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                              "client sent SYN_STREAM frame with header "

                              "block encoded using wrong dictionary: %ul",

                              (u_long) sc->zstream_in.adler);

                return ngx_http_spdy_state_protocol_error(sc);

            }

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "inflateSetDictionary() failed: %d", z);

            return ngx_http_spdy_state_internal_error(sc);

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "spdy inflateSetDictionary(): %d", z);

        z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH)

                                    : Z_OK;

    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                   sc->zstream_in.next_in, sc->zstream_in.next_out,

                   sc->zstream_in.avail_in, sc->zstream_in.avail_out,

                   z);

    if (z != Z_OK) {

        return ngx_http_spdy_state_inflate_error(sc, z);

    }

    sc->length -= sc->zstream_in.next_in - pos;

    pos = sc->zstream_in.next_in;

    buf->last = sc->zstream_in.next_out;

    if (r->headers_in.headers.part.elts == NULL) {

        if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {

            if (sc->length == 0) {

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "premature end of spdy header block");

                return ngx_http_spdy_state_headers_error(sc, pos, end);

            }

            return ngx_http_spdy_state_save(sc, pos, end,

                                            ngx_http_spdy_state_headers);

        }

        sc->entries = ngx_spdy_frame_parse_uint32(buf->pos);

        buf->pos += NGX_SPDY_NV_NUM_SIZE;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "spdy header block has %ui entries",

                       sc->entries);

        if (ngx_list_init(&r->headers_in.headers, r->pool, 20,

                          sizeof(ngx_table_elt_t))

            != NGX_OK)

        {

            ngx_http_spdy_close_stream(sc->stream,

                                       NGX_HTTP_INTERNAL_SERVER_ERROR);

            return ngx_http_spdy_state_headers_skip(sc, pos, end);

        }

        if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,

                           sizeof(ngx_table_elt_t *))

            != NGX_OK)

        {

            ngx_http_spdy_close_stream(sc->stream,

                                       NGX_HTTP_INTERNAL_SERVER_ERROR);

            return ngx_http_spdy_state_headers_skip(sc, pos, end);

        }

    }

    while (sc->entries) {

        rc = ngx_http_spdy_parse_header(r);

        switch (rc) {

        case NGX_DONE:

            sc->entries--;

        case NGX_OK:

            break;

        case NGX_AGAIN:

            if (sc->zstream_in.avail_in) {

                rc = ngx_http_spdy_alloc_large_header_buffer(r);

                if (rc == NGX_DECLINED) {

                    ngx_http_finalize_request(r,

                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);

                    return ngx_http_spdy_state_headers_skip(sc, pos, end);

                }

                if (rc != NGX_OK) {

                    ngx_http_spdy_close_stream(sc->stream,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                    return ngx_http_spdy_state_headers_skip(sc, pos, end);

                }

                /* null-terminate the last processed header name or value */

                *buf->pos = '\0';

                buf = r->header_in;

                sc->zstream_in.next_out = buf->last;

                /* one byte is reserved for null-termination */

                sc->zstream_in.avail_out = buf->end - buf->last - 1;

                z = inflate(&sc->zstream_in, Z_NO_FLUSH);

                ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                           sc->zstream_in.next_in, sc->zstream_in.next_out,

                           sc->zstream_in.avail_in, sc->zstream_in.avail_out,

                           z);

                if (z != Z_OK) {

                    return ngx_http_spdy_state_inflate_error(sc, z);

                }

                sc->length -= sc->zstream_in.next_in - pos;

                pos = sc->zstream_in.next_in;

                buf->last = sc->zstream_in.next_out;

                continue;

            }

            if (sc->length == 0) {

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "premature end of spdy header block");

                return ngx_http_spdy_state_headers_error(sc, pos, end);

            }

            return ngx_http_spdy_state_save(sc, pos, end,

                                            ngx_http_spdy_state_headers);

        case NGX_HTTP_PARSE_INVALID_HEADER:

            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

            return ngx_http_spdy_state_headers_skip(sc, pos, end);

        default: /* NGX_ERROR */

            return ngx_http_spdy_state_headers_error(sc, pos, end);

        }

        /* a header line has been parsed successfully */

        rc = ngx_http_spdy_handle_request_header(r);

        if (rc != NGX_OK) {

            if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {

                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

                return ngx_http_spdy_state_headers_skip(sc, pos, end);

            }

            if (rc != NGX_ABORT) {

                ngx_http_spdy_close_stream(sc->stream,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

            }

            return ngx_http_spdy_state_headers_skip(sc, pos, end);

        }

    }

    if (buf->pos != buf->last || sc->zstream_in.avail_in) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "incorrect number of spdy header block entries");

        return ngx_http_spdy_state_headers_error(sc, pos, end);

    }

    if (sc->length) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_headers);

    }

    /* null-terminate the last header value */

    *buf->pos = '\0';

    ngx_http_spdy_run_request(r);

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    int     n;

    size_t  size;

    u_char  buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE];

    if (sc->length == 0) {

        return ngx_http_spdy_state_complete(sc, pos, end);

    }

    size = end - pos;

    if (size == 0) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_headers_skip);

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy header block skip %uz of %uz", size, sc->length);

    sc->zstream_in.next_in = pos;

    sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length;

    while (sc->zstream_in.avail_in) {

        sc->zstream_in.next_out = buffer;

        sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE;

        n = inflate(&sc->zstream_in, Z_NO_FLUSH);

        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                       sc->zstream_in.next_in, sc->zstream_in.next_out,

                       sc->zstream_in.avail_in, sc->zstream_in.avail_out,

                       n);

        if (n != Z_OK) {

            return ngx_http_spdy_state_inflate_error(sc, n);

        }

    }

    pos = sc->zstream_in.next_in;

    if (size < sc->length) {

        sc->length -= size;

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_headers_skip);

    }

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_http_spdy_stream_t  *stream;

    stream = sc->stream;

    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                  "client sent SYN_STREAM frame for stream %ui "

                  "with invalid header block", stream->id);

    if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR,

                                      stream->priority)

        != NGX_OK)

    {

        return ngx_http_spdy_state_internal_error(sc);

    }

    stream->out_closed = 1;

    ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST);

    return ngx_http_spdy_state_headers_skip(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    size_t                   delta;

    ngx_uint_t               sid;

    ngx_event_t             *wev;

    ngx_queue_t             *q;

    ngx_http_spdy_stream_t  *stream;

    if (end - pos < NGX_SPDY_WINDOW_UPDATE_SIZE) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_window_update);

    }

    if (sc->length != NGX_SPDY_WINDOW_UPDATE_SIZE) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent WINDOW_UPDATE frame "

                      "with incorrect length %uz", sc->length);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    sid = ngx_spdy_frame_parse_sid(pos);

    pos += NGX_SPDY_SID_SIZE;

    delta = ngx_spdy_frame_parse_delta(pos);

    pos += NGX_SPDY_DELTA_SIZE;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy WINDOW_UPDATE sid:%ui delta:%ui", sid, delta);

    if (sid) {

        stream = ngx_http_spdy_get_stream_by_id(sc, sid);

        if (stream == NULL) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                           "unknown spdy stream");

            return ngx_http_spdy_state_complete(sc, pos, end);

        }

        if (stream->send_window > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) {

            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                          "client violated flow control for stream %ui: "

                          "received WINDOW_UPDATE frame with delta %uz "

                          "not allowed for window %z",

                          sid, delta, stream->send_window);

            if (ngx_http_spdy_terminate_stream(sc, stream,

                                               NGX_SPDY_FLOW_CONTROL_ERROR)

                == NGX_ERROR)

            {

                return ngx_http_spdy_state_internal_error(sc);

            }

            return ngx_http_spdy_state_complete(sc, pos, end);

        }

        stream->send_window += delta;

        if (stream->exhausted) {

            stream->exhausted = 0;

            wev = stream->request->connection->write;

            if (!wev->timer_set) {

                wev->delayed = 0;

                wev->handler(wev);

            }

        }

    } else {

        sc->send_window += delta;

        if (sc->send_window > NGX_SPDY_MAX_WINDOW) {

            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                          "client violated connection flow control: "

                          "received WINDOW_UPDATE frame with delta %uz "

                          "not allowed for window %uz",

                          delta, sc->send_window);

            return ngx_http_spdy_state_protocol_error(sc);

        }

        while (!ngx_queue_empty(&sc->waiting)) {

            q = ngx_queue_head(&sc->waiting);

            ngx_queue_remove(q);

            stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);

            stream->handled = 0;

            wev = stream->request->connection->write;

            if (!wev->timer_set) {

                wev->delayed = 0;

                wev->handler(wev);

                if (sc->send_window == 0) {

                    break;

                }

            }

        }

    }

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_http_spdy_stream_t  *stream;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy DATA frame");

    if (sc->length > sc->recv_window) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client violated connection flow control: "

                      "received DATA frame length %uz, available window %uz",

                      sc->length, sc->recv_window);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    sc->recv_window -= sc->length;

    if (sc->recv_window < NGX_SPDY_MAX_WINDOW / 4) {

        if (ngx_http_spdy_send_window_update(sc, 0,

                                             NGX_SPDY_MAX_WINDOW

                                             - sc->recv_window)

            == NGX_ERROR)

        {

            return ngx_http_spdy_state_internal_error(sc);

        }

        sc->recv_window = NGX_SPDY_MAX_WINDOW;

    }

    stream = sc->stream;

    if (stream == NULL) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "unknown spdy stream");

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    if (sc->length > stream->recv_window) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client violated flow control for stream %ui: "

                      "received DATA frame length %uz, available window %uz",

                      stream->id, sc->length, stream->recv_window);

        if (ngx_http_spdy_terminate_stream(sc, stream,

                                           NGX_SPDY_FLOW_CONTROL_ERROR)

            == NGX_ERROR)

        {

            return ngx_http_spdy_state_internal_error(sc);

        }

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    stream->recv_window -= sc->length;

    if (stream->recv_window < NGX_SPDY_STREAM_WINDOW / 4) {

        if (ngx_http_spdy_send_window_update(sc, stream->id,

                                             NGX_SPDY_STREAM_WINDOW

                                             - stream->recv_window)

            == NGX_ERROR)

        {

            return ngx_http_spdy_state_internal_error(sc);

        }

        stream->recv_window = NGX_SPDY_STREAM_WINDOW;

    }

    if (stream->in_closed) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent DATA frame for half-closed stream %ui",

                      stream->id);

        if (ngx_http_spdy_terminate_stream(sc, stream,

                                           NGX_SPDY_STREAM_ALREADY_CLOSED)

            == NGX_ERROR)

        {

            return ngx_http_spdy_state_internal_error(sc);

        }

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    return ngx_http_spdy_state_read_data(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    size_t                     size;

    ssize_t                    n;

    ngx_buf_t                 *buf;

    ngx_int_t                  rc;

    ngx_temp_file_t           *tf;

    ngx_http_request_t        *r;

    ngx_http_spdy_stream_t    *stream;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    stream = sc->stream;

    if (stream == NULL) {

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    if (stream->skip_data) {

        if (sc->flags & NGX_SPDY_FLAG_FIN) {

            stream->in_closed = 1;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "skipping spdy DATA frame, reason: %d",

                       stream->skip_data);

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    size = end - pos;

    if (size > sc->length) {

        size = sc->length;

    }

    r = stream->request;

    if (r->request_body == NULL

        && ngx_http_spdy_init_request_body(r) != NGX_OK)

    {

        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;

        return ngx_http_spdy_state_skip(sc, pos, end);

    }

    rb = r->request_body;

    tf = rb->temp_file;

    buf = rb->buf;

    if (size) {

        rb->rest += size;

        if (r->headers_in.content_length_n != -1

            && r->headers_in.content_length_n < rb->rest)

        {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client intended to send body data "

                          "larger than declared");

            stream->skip_data = NGX_SPDY_DATA_ERROR;

            goto error;

        } else {

            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

            if (clcf->client_max_body_size

                && clcf->client_max_body_size < rb->rest)

            {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "client intended to send "

                              "too large chunked body: %O bytes", rb->rest);

                stream->skip_data = NGX_SPDY_DATA_ERROR;

                goto error;

            }

        }

        sc->length -= size;

        if (tf) {

            buf->start = pos;

            buf->pos = pos;

            pos += size;

            buf->end = pos;

            buf->last = pos;

            n = ngx_write_chain_to_temp_file(tf, rb->bufs);

            /* TODO: n == 0 or not complete and level event */

            if (n == NGX_ERROR) {

                stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;

                goto error;

            }

            tf->offset += n;

        } else {

            buf->last = ngx_cpymem(buf->last, pos, size);

            pos += size;

        }

        r->request_length += size;

    }

    if (sc->length) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_read_data);

    }

    if (sc->flags & NGX_SPDY_FLAG_FIN) {

        stream->in_closed = 1;

        if (r->headers_in.content_length_n < 0) {

            r->headers_in.content_length_n = rb->rest;

        } else if (r->headers_in.content_length_n != rb->rest) {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client prematurely closed stream: "

                          "only %O out of %O bytes of request body received",

                          rb->rest, r->headers_in.content_length_n);

            stream->skip_data = NGX_SPDY_DATA_ERROR;

            goto error;

        }

        if (tf) {

            ngx_memzero(buf, sizeof(ngx_buf_t));

            buf->in_file = 1;

            buf->file_last = tf->file.offset;

            buf->file = &tf->file;

            rb->buf = NULL;

        }

        if (rb->post_handler) {

            r->read_event_handler = ngx_http_block_reading;

            rb->post_handler(r);

        }

    }

    return ngx_http_spdy_state_complete(sc, pos, end);

error:

    if (rb->post_handler) {

        if (stream->skip_data == NGX_SPDY_DATA_ERROR) {

            rc = (r->headers_in.content_length_n == -1)

                 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE

                 : NGX_HTTP_BAD_REQUEST;

        } else {

            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_http_finalize_request(r, rc);

    }

    return ngx_http_spdy_state_skip(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_uint_t               sid, status;

    ngx_event_t             *ev;

    ngx_connection_t        *fc;

    ngx_http_spdy_stream_t  *stream;

    if (end - pos < NGX_SPDY_RST_STREAM_SIZE) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_rst_stream);

    }

    if (sc->length != NGX_SPDY_RST_STREAM_SIZE) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent RST_STREAM frame with incorrect length %uz",

                      sc->length);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    sid = ngx_spdy_frame_parse_sid(pos);

    pos += NGX_SPDY_SID_SIZE;

    status = ngx_spdy_frame_parse_uint32(pos);

    pos += sizeof(uint32_t);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy RST_STREAM sid:%ui st:%ui", sid, status);

    stream = ngx_http_spdy_get_stream_by_id(sc, sid);

    if (stream == NULL) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "unknown spdy stream");

        return ngx_http_spdy_state_complete(sc, pos, end);

    }

    stream->in_closed = 1;

    stream->out_closed = 1;

    fc = stream->request->connection;

    fc->error = 1;

    switch (status) {

    case NGX_SPDY_CANCEL:

        ngx_log_error(NGX_LOG_INFO, fc->log, 0,

                      "client canceled stream %ui", sid);

        break;

    case NGX_SPDY_INTERNAL_ERROR:

        ngx_log_error(NGX_LOG_INFO, fc->log, 0,

                      "client terminated stream %ui due to internal error",

                      sid);

        break;

    default:

        ngx_log_error(NGX_LOG_INFO, fc->log, 0,

                      "client terminated stream %ui with status %ui",

                      sid, status);

        break;

    }

    ev = fc->read;

    ev->handler(ev);

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_http_spdy_out_frame_t  *frame;

    if (end - pos < NGX_SPDY_PING_SIZE) {

        return ngx_http_spdy_state_save(sc, pos, end,

                                        ngx_http_spdy_state_ping);

    }

    if (sc->length != NGX_SPDY_PING_SIZE) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent PING frame with incorrect length %uz",

                      sc->length);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy PING frame");

    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE,

                                        NGX_SPDY_HIGHEST_PRIORITY);

    if (frame == NULL) {

        return ngx_http_spdy_state_internal_error(sc);

    }

    buf = frame->first->buf;

    p = buf->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING);

    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE);

    p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE);

    buf->last = p;

    ngx_http_spdy_queue_frame(sc, frame);

    pos += NGX_SPDY_PING_SIZE;

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    size_t  size;

    size = end - pos;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy frame skip %uz of %uz", size, sc->length);

    if (size < sc->length) {

        sc->length -= size;

        return ngx_http_spdy_state_save(sc, end, end,

                                        ngx_http_spdy_state_skip);

    }

    return ngx_http_spdy_state_complete(sc, pos + sc->length, end);

}

static u_char *

‌ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_uint_t  fid, val;

    if (sc->entries == 0) {

        if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) {

            return ngx_http_spdy_state_save(sc, pos, end,

                                            ngx_http_spdy_state_settings);

        }

        sc->entries = ngx_spdy_frame_parse_uint32(pos);

        pos += NGX_SPDY_SETTINGS_NUM_SIZE;

        sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE;

        if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) {

            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                          "client sent SETTINGS frame with incorrect "

                          "length %uz or number of entries %ui",

                          sc->length, sc->entries);

            return ngx_http_spdy_state_protocol_error(sc);

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "spdy SETTINGS frame has %ui entries", sc->entries);

    }

    while (sc->entries) {

        if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) {

            return ngx_http_spdy_state_save(sc, pos, end,

                                            ngx_http_spdy_state_settings);

        }

        sc->entries--;

        sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE;

        fid = ngx_spdy_frame_parse_uint32(pos);

        pos += NGX_SPDY_SETTINGS_FID_SIZE;

        val = ngx_spdy_frame_parse_uint32(pos);

        pos += NGX_SPDY_SETTINGS_VAL_SIZE;

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                       "spdy SETTINGS entry fl:%ui id:%ui val:%ui",

                       ngx_spdy_frame_flags(fid), ngx_spdy_frame_id(fid), val);

        if (ngx_spdy_frame_flags(fid) == NGX_SPDY_SETTINGS_FLAG_PERSISTED) {

            continue;

        }

        switch (ngx_spdy_frame_id(fid)) {

        case NGX_SPDY_SETTINGS_INIT_WINDOW:

            if (val > NGX_SPDY_MAX_WINDOW) {

                ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                              "client sent SETTINGS frame with "

                              "incorrect INIT_WINDOW value: %ui", val);

                return ngx_http_spdy_state_protocol_error(sc);

            }

            if (ngx_http_spdy_adjust_windows(sc, val - sc->init_window)

                != NGX_OK)

            {

                return ngx_http_spdy_state_internal_error(sc);

            }

            sc->init_window = val;

            continue;

        }

    }

    return ngx_http_spdy_state_complete(sc, pos, end);

}

static u_char *

‌ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos,

    u_char *end)

{

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy frame complete pos:%p end:%p", pos, end);

    if (pos > end) {

        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,

                      "receive buffer overrun");

        ngx_debug_point();

        return ngx_http_spdy_state_internal_error(sc);

    }

    sc->handler = ngx_http_spdy_state_head;

    sc->stream = NULL;

    return pos;

}

static u_char *

‌ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler)

{

    size_t  size;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy frame state save pos:%p end:%p handler:%p",

                   pos, end, handler);

    size = end - pos;

    if (size > NGX_SPDY_STATE_BUFFER_SIZE) {

        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,

                      "state buffer overflow: %uz bytes required", size);

        ngx_debug_point();

        return ngx_http_spdy_state_internal_error(sc);

    }

    ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE);

    sc->buffer_used = size;

    sc->handler = handler;

    sc->incomplete = 1;

    return end;

}

static u_char *

‌ngx_http_spdy_state_inflate_error(ngx_http_spdy_connection_t *sc, int rc)

{

    if (rc == Z_DATA_ERROR || rc == Z_STREAM_END) {

        ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,

                      "client sent SYN_STREAM frame with "

                      "corrupted header block, inflate() failed: %d", rc);

        return ngx_http_spdy_state_protocol_error(sc);

    }

    ngx_log_error(NGX_LOG_ERR, sc->connection->log, 0,

                  "inflate() failed: %d", rc);

    return ngx_http_spdy_state_internal_error(sc);

}

static u_char *

‌ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy state protocol error");

    if (sc->stream) {

        sc->stream->out_closed = 1;

        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);

    }

    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);

    return NULL;

}

static u_char *

‌ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy state internal error");

    if (sc->stream) {

        sc->stream->out_closed = 1;

        ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);

    }

    ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);

    return NULL;

}

static ngx_int_t

‌ngx_http_spdy_send_window_update(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,

    ngx_uint_t delta)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_http_spdy_out_frame_t  *frame;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy send WINDOW_UPDATE sid:%ui delta:%ui", sid, delta);

    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_WINDOW_UPDATE_SIZE,

                                        NGX_SPDY_HIGHEST_PRIORITY);

    if (frame == NULL) {

        return NGX_ERROR;

    }

    buf = frame->first->buf;

    p = buf->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_WINDOW_UPDATE);

    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_WINDOW_UPDATE_SIZE);

    p = ngx_spdy_frame_write_sid(p, sid);

    p = ngx_spdy_frame_aligned_write_uint32(p, delta);

    buf->last = p;

    ngx_http_spdy_queue_frame(sc, frame);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,

    ngx_uint_t status, ngx_uint_t priority)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_http_spdy_out_frame_t  *frame;

    if (sc->connection->error) {

        return NGX_OK;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy send RST_STREAM sid:%ui st:%ui", sid, status);

    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE,

                                        priority);

    if (frame == NULL) {

        return NGX_ERROR;

    }

    buf = frame->first->buf;

    p = buf->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM);

    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE);

    p = ngx_spdy_frame_write_sid(p, sid);

    p = ngx_spdy_frame_aligned_write_uint32(p, status);

    buf->last = p;

    ngx_http_spdy_queue_frame(sc, frame);

    return NGX_OK;

}

#if 0

static ngx_int_t

ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_http_spdy_out_frame_t  *frame;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy send GOAWAY sid:%ui", sc->last_sid);

    frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE,

                                        NGX_SPDY_HIGHEST_PRIORITY);

    if (frame == NULL) {

        return NGX_ERROR;

    }

    buf = frame->first->buf;

    p = buf->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY);

    p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE);

    p = ngx_spdy_frame_write_sid(p, sc->last_sid);

    buf->last = p;

    ngx_http_spdy_queue_frame(sc, frame);

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_chain_t                *cl;

    ngx_http_spdy_srv_conf_t   *sscf;

    ngx_http_spdy_out_frame_t  *frame;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy send SETTINGS frame");

    frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));

    if (frame == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(sc->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE

                                        + NGX_SPDY_SETTINGS_NUM_SIZE

                                        + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);

    if (buf == NULL) {

        return NGX_ERROR;

    }

    buf->last_buf = 1;

    cl->buf = buf;

    cl->next = NULL;

    frame->first = cl;

    frame->last = cl;

    frame->handler = ngx_http_spdy_settings_frame_handler;

    frame->stream = NULL;

#if (NGX_DEBUG)

    frame->length = NGX_SPDY_SETTINGS_NUM_SIZE

                    + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE;

#endif

    frame->priority = NGX_SPDY_HIGHEST_PRIORITY;

    frame->blocked = 0;

    p = buf->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS);

    p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS,

                                           NGX_SPDY_SETTINGS_NUM_SIZE

                                           + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);

    p = ngx_spdy_frame_aligned_write_uint32(p, 2);

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_MAX_STREAMS);

    p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams);

    p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_INIT_WINDOW);

    p = ngx_spdy_frame_aligned_write_uint32(p, NGX_SPDY_STREAM_WINDOW);

    buf->last = p;

    ngx_http_spdy_queue_frame(sc, frame);

    return NGX_OK;

}

ngx_int_t

‌ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_buf_t  *buf;

    buf = frame->first->buf;

    if (buf->pos != buf->last) {

        return NGX_AGAIN;

    }

    ngx_free_chain(sc->pool, frame->first);

    return NGX_OK;

}

static ngx_http_spdy_out_frame_t *

‌ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length,

    ngx_uint_t priority)

{

    ngx_chain_t                *cl;

    ngx_http_spdy_out_frame_t  *frame;

    frame = sc->free_ctl_frames;

    if (frame) {

        sc->free_ctl_frames = frame->next;

        cl = frame->first;

        cl->buf->pos = cl->buf->start;

    } else {

        frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));

        if (frame == NULL) {

            return NULL;

        }

        cl = ngx_alloc_chain_link(sc->pool);

        if (cl == NULL) {

            return NULL;

        }

        cl->buf = ngx_create_temp_buf(sc->pool,

                                      NGX_SPDY_CTL_FRAME_BUFFER_SIZE);

        if (cl->buf == NULL) {

            return NULL;

        }

        cl->buf->last_buf = 1;

        frame->first = cl;

        frame->last = cl;

        frame->handler = ngx_http_spdy_ctl_frame_handler;

        frame->stream = NULL;

    }

#if (NGX_DEBUG)

    if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) {

        ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0,

                      "requested control frame is too large: %uz", length);

        return NULL;

    }

    frame->length = length;

#endif

    frame->priority = priority;

    frame->blocked = 0;

    return frame;

}

static ngx_int_t

‌ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_buf_t  *buf;

    buf = frame->first->buf;

    if (buf->pos != buf->last) {

        return NGX_AGAIN;

    }

    frame->next = sc->free_ctl_frames;

    sc->free_ctl_frames = frame;

    return NGX_OK;

}

static ngx_http_spdy_stream_t *

‌ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id,

    ngx_uint_t priority)

{

    ngx_log_t                 *log;

    ngx_uint_t                 index;

    ngx_event_t               *rev, *wev;

    ngx_connection_t          *fc;

    ngx_http_log_ctx_t        *ctx;

    ngx_http_request_t        *r;

    ngx_http_spdy_stream_t    *stream;

    ngx_http_core_srv_conf_t  *cscf;

    ngx_http_spdy_srv_conf_t  *sscf;

    fc = sc->free_fake_connections;

    if (fc) {

        sc->free_fake_connections = fc->data;

        rev = fc->read;

        wev = fc->write;

        log = fc->log;

        ctx = log->data;

    } else {

        fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t));

        if (fc == NULL) {

            return NULL;

        }

        rev = ngx_palloc(sc->pool, sizeof(ngx_event_t));

        if (rev == NULL) {

            return NULL;

        }

        wev = ngx_palloc(sc->pool, sizeof(ngx_event_t));

        if (wev == NULL) {

            return NULL;

        }

        log = ngx_palloc(sc->pool, sizeof(ngx_log_t));

        if (log == NULL) {

            return NULL;

        }

        ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t));

        if (ctx == NULL) {

            return NULL;

        }

        ctx->connection = fc;

        ctx->request = NULL;

    }

    ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t));

    log->data = ctx;

    ngx_memzero(rev, sizeof(ngx_event_t));

    rev->data = fc;

    rev->ready = 1;

    rev->handler = ngx_http_spdy_close_stream_handler;

    rev->log = log;

    ngx_memcpy(wev, rev, sizeof(ngx_event_t));

    wev->write = 1;

    ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t));

    fc->data = sc->http_connection;

    fc->read = rev;

    fc->write = wev;

    fc->sent = 0;

    fc->log = log;

    fc->buffered = 0;

    fc->sndlowat = 1;

    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;

    r = ngx_http_create_request(fc);

    if (r == NULL) {

        return NULL;

    }

    r->valid_location = 1;

    fc->data = r;

    sc->connection->requests++;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    r->header_in = ngx_create_temp_buf(r->pool,

                                       cscf->client_header_buffer_size);

    if (r->header_in == NULL) {

        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NULL;

    }

    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;

    stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t));

    if (stream == NULL) {

        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NULL;

    }

    r->spdy_stream = stream;

    stream->id = id;

    stream->request = r;

    stream->connection = sc;

    stream->send_window = sc->init_window;

    stream->recv_window = NGX_SPDY_STREAM_WINDOW;

    stream->priority = priority;

    sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);

    index = ngx_http_spdy_stream_index(sscf, id);

    stream->index = sc->streams_index[index];

    sc->streams_index[index] = stream;

    sc->processing++;

    return stream;

}

static ngx_http_spdy_stream_t *

‌ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc,

    ngx_uint_t sid)

{

    ngx_http_spdy_stream_t    *stream;

    ngx_http_spdy_srv_conf_t  *sscf;

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)];

    while (stream) {

        if (stream->id == sid) {

            return stream;

        }

        stream = stream->index;

    }

    return NULL;

}

static ngx_int_t

‌ngx_http_spdy_parse_header(ngx_http_request_t *r)

{

    u_char                     *p, *end, ch;

    ngx_uint_t                  hash;

    ngx_http_core_srv_conf_t   *cscf;

    enum {

        sw_name_len = 0,

        sw_name,

        sw_value_len,

        sw_value

    } state;

    state = r->state;

    p = r->header_in->pos;

    end = r->header_in->last;

    switch (state) {

    case sw_name_len:

        if (end - p < NGX_SPDY_NV_NLEN_SIZE) {

            return NGX_AGAIN;

        }

        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);

        if (r->lowcase_index == 0) {

            return NGX_ERROR;

        }

        /* null-terminate the previous header value */

        *p = '\0';

        p += NGX_SPDY_NV_NLEN_SIZE;

        r->invalid_header = 0;

        state = sw_name;

        /* fall through */

    case sw_name:

        if ((ngx_uint_t) (end - p) < r->lowcase_index) {

            break;

        }

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        r->header_name_start = p;

        r->header_name_end = p + r->lowcase_index;

        if (p[0] == ':') {

            p++;

        }

        hash = 0;

        for ( /* void */ ; p != r->header_name_end; p++) {

            ch = *p;

            hash = ngx_hash(hash, ch);

            if ((ch >= 'a' && ch <= 'z')

                || (ch == '-')

                || (ch >= '0' && ch <= '9')

                || (ch == '_' && cscf->underscores_in_headers))

            {

                continue;

            }

            switch (ch) {

            case '\0':

            case LF:

            case CR:

            case ':':

                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                              "client sent invalid header name: \"%*s\"",

                              r->lowcase_index, r->header_name_start);

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

            if (ch >= 'A' && ch <= 'Z') {

                return NGX_ERROR;

            }

            r->invalid_header = 1;

        }

        r->header_hash = hash;

        state = sw_value_len;

        /* fall through */

    case sw_value_len:

        if (end - p < NGX_SPDY_NV_VLEN_SIZE) {

            break;

        }

        r->lowcase_index = ngx_spdy_frame_parse_uint32(p);

        /* null-terminate header name */

        *p = '\0';

        p += NGX_SPDY_NV_VLEN_SIZE;

        state = sw_value;

        /* fall through */

    case sw_value:

        if ((ngx_uint_t) (end - p) < r->lowcase_index) {

            break;

        }

        r->header_start = p;

        while (r->lowcase_index--) {

            ch = *p;

            if (ch == '\0') {

                if (p == r->header_start) {

                    return NGX_ERROR;

                }

                r->header_end = p;

                r->header_in->pos = p + 1;

                r->state = sw_value;

                return NGX_OK;

            }

            if (ch == CR || ch == LF) {

                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                              "client sent header \"%*s\" with "

                              "invalid value: \"%*s\\%c...\"",

                              r->header_name_end - r->header_name_start,

                              r->header_name_start,

                              p - r->header_start,

                              r->header_start,

                              ch == CR ? 'r' : 'n');

                return NGX_HTTP_PARSE_INVALID_HEADER;

            }

            p++;

        }

        r->header_end = p;

        r->header_in->pos = p;

        r->state = 0;

        return NGX_DONE;

    }

    r->header_in->pos = p;

    r->state = state;

    return NGX_AGAIN;

}

static ngx_int_t

‌ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)

{

    u_char                    *old, *new, *p;

    size_t                     rest;

    ngx_buf_t                 *buf;

    ngx_http_spdy_stream_t    *stream;

    ngx_http_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy alloc large header buffer");

    stream = r->spdy_stream;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    if (stream->header_buffers

        == (ngx_uint_t) cscf->large_client_header_buffers.num)

    {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent too large request");

        return NGX_DECLINED;

    }

    rest = r->header_in->last - r->header_in->pos;

    /*

     * One more byte is needed for null-termination

     * and another one for further progress.

     */

    if (rest > cscf->large_client_header_buffers.size - 2) {

        p = r->header_in->pos;

        if (rest > NGX_MAX_ERROR_STR - 300) {

            rest = NGX_MAX_ERROR_STR - 300;

        }

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent too long header name or value: \"%*s...\"",

                      rest, p);

        return NGX_DECLINED;

    }

    buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size);

    if (buf == NULL) {

        return NGX_ERROR;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy large header alloc: %p %uz",

                   buf->pos, buf->end - buf->last);

    old = r->header_in->pos;

    new = buf->pos;

    if (rest) {

        buf->last = ngx_cpymem(new, old, rest);

    }

    r->header_in = buf;

    stream->header_buffers++;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_handle_request_header(ngx_http_request_t *r)

{

    ngx_uint_t                       i;

    ngx_table_elt_t                 *h;

    ngx_http_core_srv_conf_t        *cscf;

    ngx_http_spdy_request_header_t  *sh;

    if (r->invalid_header) {

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        if (cscf->ignore_invalid_headers) {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client sent invalid header: \"%*s\"",

                          r->header_end - r->header_name_start,

                          r->header_name_start);

            return NGX_OK;

        }

    }

    if (r->header_name_start[0] == ':') {

        r->header_name_start++;

        for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {

            sh = &ngx_http_spdy_request_headers[i];

            if (sh->hash != r->header_hash

                || sh->len != r->header_name_end - r->header_name_start

                || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)

            {

                continue;

            }

            return sh->handler(r);

        }

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent invalid header name: \":%*s\"",

                      r->header_end - r->header_name_start,

                      r->header_name_start);

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    h = ngx_list_push(&r->headers_in.headers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    h->hash = r->header_hash;

    h->key.len = r->header_name_end - r->header_name_start;

    h->key.data = r->header_name_start;

    h->value.len = r->header_end - r->header_start;

    h->value.data = r->header_start;

    h->lowcase_key = h->key.data;

    return NGX_OK;

}

void

‌ngx_http_spdy_request_headers_init(void)

{

    ngx_uint_t                       i;

    ngx_http_spdy_request_header_t  *h;

    for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {

        h = &ngx_http_spdy_request_headers[i];

        h->hash = ngx_hash_key(h->header, h->len);

    }

}

static ngx_int_t

‌ngx_http_spdy_parse_method(ngx_http_request_t *r)

{

    size_t         k, len;

    ngx_uint_t     n;

    const u_char  *p, *m;

    /*

     * This array takes less than 256 sequential bytes,

     * and if typical CPU cache line size is 64 bytes,

     * it is prefetched for 4 load operations.

     */

    static const struct {

        u_char            len;

        const u_char      method[11];

        uint32_t          value;

    } tests[] = {

        { 3, "GET",       NGX_HTTP_GET },

        { 4, "POST",      NGX_HTTP_POST },

        { 4, "HEAD",      NGX_HTTP_HEAD },

        { 7, "OPTIONS",   NGX_HTTP_OPTIONS },

        { 8, "PROPFIND",  NGX_HTTP_PROPFIND },

        { 3, "PUT",       NGX_HTTP_PUT },

        { 5, "MKCOL",     NGX_HTTP_MKCOL },

        { 6, "DELETE",    NGX_HTTP_DELETE },

        { 4, "COPY",      NGX_HTTP_COPY },

        { 4, "MOVE",      NGX_HTTP_MOVE },

        { 9, "PROPPATCH", NGX_HTTP_PROPPATCH },

        { 4, "LOCK",      NGX_HTTP_LOCK },

        { 6, "UNLOCK",    NGX_HTTP_UNLOCK },

        { 5, "PATCH",     NGX_HTTP_PATCH },

        { 5, "TRACE",     NGX_HTTP_TRACE }

    }, *test;

    if (r->method_name.len) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent duplicate :method header");

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    len = r->header_end - r->header_start;

    r->method_name.len = len;

    r->method_name.data = r->header_start;

    test = tests;

    n = sizeof(tests) / sizeof(tests[0]);

    do {

        if (len == test->len) {

            p = r->method_name.data;

            m = test->method;

            k = len;

            do {

                if (*p++ != *m++) {

                    goto next;

                }

            } while (--k);

            r->method = test->value;

            return NGX_OK;

        }

    next:

        test++;

    } while (--n);

    p = r->method_name.data;

    do {

        if ((*p < 'A' || *p > 'Z') && *p != '_') {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client sent invalid method: \"%V\"",

                          &r->method_name);

            return NGX_HTTP_PARSE_INVALID_HEADER;

        }

        p++;

    } while (--len);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_parse_scheme(ngx_http_request_t *r)

{

    if (r->schema_start) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent duplicate :schema header");

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    r->schema_start = r->header_start;

    r->schema_end = r->header_end;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_parse_host(ngx_http_request_t *r)

{

    ngx_table_elt_t  *h;

    if (r->headers_in.host) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent duplicate :host header");

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    h = ngx_list_push(&r->headers_in.headers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    r->headers_in.host = h;

    h->hash = r->header_hash;

    h->key.len = r->header_name_end - r->header_name_start;

    h->key.data = r->header_name_start;

    h->value.len = r->header_end - r->header_start;

    h->value.data = r->header_start;

    h->lowcase_key = h->key.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_parse_path(ngx_http_request_t *r)

{

    if (r->unparsed_uri.len) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent duplicate :path header");

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    r->uri_start = r->header_start;

    r->uri_end = r->header_end;

    if (ngx_http_parse_uri(r) != NGX_OK) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent invalid URI: \"%*s\"",

                      r->uri_end - r->uri_start, r->uri_start);

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    if (ngx_http_process_request_uri(r) != NGX_OK) {

        /*

         * request has been finalized already

         * in ngx_http_process_request_uri()

         */

        return NGX_ABORT;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_parse_version(ngx_http_request_t *r)

{

    u_char  *p, ch;

    if (r->http_protocol.len) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent duplicate :version header");

        return NGX_HTTP_PARSE_INVALID_HEADER;

    }

    p = r->header_start;

    if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {

        goto invalid;

    }

    ch = *(p + 5);

    if (ch < '1' || ch > '9') {

        goto invalid;

    }

    r->http_major = ch - '0';

    for (p += 6; p != r->header_end - 2; p++) {

        ch = *p;

        if (ch == '.') {

            break;

        }

        if (ch < '0' || ch > '9') {

            goto invalid;

        }

        r->http_major = r->http_major * 10 + ch - '0';

    }

    if (*p != '.') {

        goto invalid;

    }

    ch = *(p + 1);

    if (ch < '0' || ch > '9') {

        goto invalid;

    }

    r->http_minor = ch - '0';

    for (p += 2; p != r->header_end; p++) {

        ch = *p;

        if (ch < '0' || ch > '9') {

            goto invalid;

        }

        r->http_minor = r->http_minor * 10 + ch - '0';

    }

    r->http_protocol.len = r->header_end - r->header_start;

    r->http_protocol.data = r->header_start;

    r->http_version = r->http_major * 1000 + r->http_minor;

    return NGX_OK;

invalid:

    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                  "client sent invalid http version: \"%*s\"",

                  r->header_end - r->header_start, r->header_start);

    return NGX_HTTP_PARSE_INVALID_HEADER;

}

static ngx_int_t

‌ngx_http_spdy_construct_request_line(ngx_http_request_t *r)

{

    u_char  *p;

    if (r->method_name.len == 0

        || r->unparsed_uri.len == 0

        || r->http_protocol.len == 0)

    {

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return NGX_ERROR;

    }

    r->request_line.len = r->method_name.len + 1

                          + r->unparsed_uri.len + 1

                          + r->http_protocol.len;

    p = ngx_pnalloc(r->pool, r->request_line.len + 1);

    if (p == NULL) {

        ngx_http_spdy_close_stream(r->spdy_stream,

                                   NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_ERROR;

    }

    r->request_line.data = p;

    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);

    *p++ = ' ';

    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);

    *p++ = ' ';

    ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1);

    /* some modules expect the space character after method name */

    r->method_name.data = r->request_line.data;

    return NGX_OK;

}

static void

‌ngx_http_spdy_run_request(ngx_http_request_t *r)

{

    ngx_uint_t                  i;

    ngx_list_part_t            *part;

    ngx_table_elt_t            *h;

    ngx_http_header_t          *hh;

    ngx_http_core_main_conf_t  *cmcf;

    if (ngx_http_spdy_construct_request_line(r) != NGX_OK) {

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy http request line: \"%V\"", &r->request_line);

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    part = &r->headers_in.headers.part;

    h = part->elts;

    for (i = 0 ;; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            h = part->elts;

            i = 0;

        }

        hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash,

                           h[i].lowcase_key, h[i].key.len);

        if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) {

            return;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "spdy http header: \"%V: %V\"", &h[i].key, &h[i].value);

    }

    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

    if (ngx_http_process_request_header(r) != NGX_OK) {

        return;

    }

    if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client prematurely closed stream");

        r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR;

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return;

    }

    ngx_http_process_request(r);

}

static ngx_int_t

‌ngx_http_spdy_init_request_body(ngx_http_request_t *r)

{

    ngx_buf_t                 *buf;

    ngx_temp_file_t           *tf;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

    if (rb == NULL) {

        return NGX_ERROR;

    }

    r->request_body = rb;

    if (r->spdy_stream->in_closed) {

        return NGX_OK;

    }

    rb->rest = r->headers_in.content_length_n;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->request_body_in_file_only

        || rb->rest > (off_t) clcf->client_body_buffer_size

        || rb->rest < 0)

    {

        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

        if (tf == NULL) {

            return NGX_ERROR;

        }

        tf->file.fd = NGX_INVALID_FILE;

        tf->file.log = r->connection->log;

        tf->path = clcf->client_body_temp_path;

        tf->pool = r->pool;

        tf->warn = "a client request body is buffered to a temporary file";

        tf->log_level = r->request_body_file_log_level;

        tf->persistent = r->request_body_in_persistent_file;

        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {

            tf->access = 0660;

        }

        rb->temp_file = tf;

        if (r->spdy_stream->in_closed

            && ngx_create_temp_file(&tf->file, tf->path, tf->pool,

                                    tf->persistent, tf->clean, tf->access)

               != NGX_OK)

        {

            return NGX_ERROR;

        }

        buf = ngx_calloc_buf(r->pool);

        if (buf == NULL) {

            return NGX_ERROR;

        }

    } else {

        if (rb->rest == 0) {

            return NGX_OK;

        }

        buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);

        if (buf == NULL) {

            return NGX_ERROR;

        }

    }

    rb->buf = buf;

    rb->bufs = ngx_alloc_chain_link(r->pool);

    if (rb->bufs == NULL) {

        return NGX_ERROR;

    }

    rb->bufs->buf = buf;

    rb->bufs->next = NULL;

    rb->rest = 0;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_spdy_read_request_body(ngx_http_request_t *r,

    ngx_http_client_body_handler_pt post_handler)

{

    ngx_http_spdy_stream_t  *stream;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy read request body");

    stream = r->spdy_stream;

    switch (stream->skip_data) {

    case NGX_SPDY_DATA_DISCARD:

        post_handler(r);

        return NGX_OK;

    case NGX_SPDY_DATA_ERROR:

        if (r->headers_in.content_length_n == -1) {

            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;

        } else {

            return NGX_HTTP_BAD_REQUEST;

        }

    case NGX_SPDY_DATA_INTERNAL_ERROR:

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) {

        stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (stream->in_closed) {

        post_handler(r);

        return NGX_OK;

    }

    r->request_body->post_handler = post_handler;

    r->read_event_handler = ngx_http_test_reading;

    r->write_event_handler = ngx_http_request_empty_handler;

    return NGX_AGAIN;

}

static ngx_int_t

‌ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream, ngx_uint_t status)

{

    ngx_event_t       *rev;

    ngx_connection_t  *fc;

    if (ngx_http_spdy_send_rst_stream(sc, stream->id, status,

                                      NGX_SPDY_HIGHEST_PRIORITY)

        == NGX_ERROR)

    {

        return NGX_ERROR;

    }

    stream->out_closed = 1;

    fc = stream->request->connection;

    fc->error = 1;

    rev = fc->read;

    rev->handler(rev);

    return NGX_OK;

}

static void

‌ngx_http_spdy_close_stream_handler(ngx_event_t *ev)

{

    ngx_connection_t    *fc;

    ngx_http_request_t  *r;

    fc = ev->data;

    r = fc->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy close stream handler");

    ngx_http_spdy_close_stream(r->spdy_stream, 0);

}

void

‌ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)

{

    int                           tcp_nodelay;

    ngx_event_t                  *ev;

    ngx_connection_t             *c, *fc;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_spdy_stream_t      **index, *s;

    ngx_http_spdy_srv_conf_t     *sscf;

    ngx_http_spdy_connection_t   *sc;

    sc = stream->connection;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy close stream %ui, queued %ui, processing %ui",

                   stream->id, stream->queued, sc->processing);

    fc = stream->request->connection;

    if (stream->queued) {

        fc->write->handler = ngx_http_spdy_close_stream_handler;

        return;

    }

    if (!stream->out_closed) {

        if (ngx_http_spdy_send_rst_stream(sc, stream->id,

                                          NGX_SPDY_INTERNAL_ERROR,

                                          stream->priority)

            != NGX_OK)

        {

            sc->connection->error = 1;

        }

    } else {

        c = sc->connection;

        if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {

            if (ngx_tcp_push(c->fd) == -1) {

                ngx_connection_error(c, ngx_socket_errno,

                                     ngx_tcp_push_n " failed");

                c->error = 1;

                tcp_nodelay = 0;

            } else {

                c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;

                tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;

            }

        } else {

            tcp_nodelay = 1;

        }

        clcf = ngx_http_get_module_loc_conf(stream->request,

                                            ngx_http_core_module);

        if (tcp_nodelay

            && clcf->tcp_nodelay

            && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)

        {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");

            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,

                           (const void *) &tcp_nodelay, sizeof(int))

                == -1)

            {

#if (NGX_SOLARIS)

                /* Solaris returns EINVAL if a socket has been shut down */

                c->log_error = NGX_ERROR_IGNORE_EINVAL;

#endif

                ngx_connection_error(c, ngx_socket_errno,

                                     "setsockopt(TCP_NODELAY) failed");

                c->log_error = NGX_ERROR_INFO;

                c->error = 1;

            } else {

                c->tcp_nodelay = NGX_TCP_NODELAY_SET;

            }

        }

    }

    if (sc->stream == stream) {

        sc->stream = NULL;

    }

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id);

    for ( ;; ) {

        s = *index;

        if (s == NULL) {

            break;

        }

        if (s == stream) {

            *index = s->index;

            break;

        }

        index = &s->index;

    }

    ngx_http_free_request(stream->request, rc);

    ev = fc->read;

    if (ev->active || ev->disabled) {

        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,

                      "fake read event was activated");

    }

    if (ev->timer_set) {

        ngx_del_timer(ev);

    }

    if (ev->posted) {

        ngx_delete_posted_event(ev);

    }

    ev = fc->write;

    if (ev->active || ev->disabled) {

        ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,

                      "fake write event was activated");

    }

    if (ev->timer_set) {

        ngx_del_timer(ev);

    }

    if (ev->posted) {

        ngx_delete_posted_event(ev);

    }

    fc->data = sc->free_fake_connections;

    sc->free_fake_connections = fc;

    sc->processing--;

    if (sc->processing || sc->blocked) {

        return;

    }

    ev = sc->connection->read;

    ev->handler = ngx_http_spdy_handle_connection_handler;

    ngx_post_event(ev, &ngx_posted_events);

}

static void

‌ngx_http_spdy_handle_connection_handler(ngx_event_t *rev)

{

    ngx_connection_t  *c;

    rev->handler = ngx_http_spdy_read_handler;

    if (rev->ready) {

        ngx_http_spdy_read_handler(rev);

        return;

    }

    c = rev->data;

    ngx_http_spdy_handle_connection(c->data);

}

static void

‌ngx_http_spdy_keepalive_handler(ngx_event_t *rev)

{

    ngx_connection_t            *c;

    ngx_http_spdy_srv_conf_t    *sscf;

    ngx_http_spdy_connection_t  *sc;

    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler");

    if (rev->timedout || c->close) {

        ngx_http_close_connection(c);

        return;

    }

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        if (rev->pending_eof) {

            c->log->handler = NULL;

            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,

                          "kevent() reported that client %V closed "

                          "keepalive connection", &c->addr_text);

#if (NGX_HTTP_SSL)

            if (c->ssl) {

                c->ssl->no_send_shutdown = 1;

            }

#endif

            ngx_http_close_connection(c);

            return;

        }

    }

#endif

    c->destroyed = 0;

    c->idle = 0;

    ngx_reusable_connection(c, 0);

    sc = c->data;

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);

    if (sc->pool == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    sc->streams_index = ngx_pcalloc(sc->pool,

                                    ngx_http_spdy_streams_index_size(sscf)

                                    * sizeof(ngx_http_spdy_stream_t *));

    if (sc->streams_index == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    c->write->handler = ngx_http_spdy_write_handler;

    rev->handler = ngx_http_spdy_read_handler;

    ngx_http_spdy_read_handler(rev);

}

static void

‌ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,

    ngx_int_t rc)

{

    ngx_uint_t                 i, size;

    ngx_event_t               *ev;

    ngx_connection_t          *c, *fc;

    ngx_http_request_t        *r;

    ngx_http_spdy_stream_t    *stream;

    ngx_http_spdy_srv_conf_t  *sscf;

    c = sc->connection;

    if (!sc->processing) {

        ngx_http_close_connection(c);

        return;

    }

    c->error = 1;

    c->read->handler = ngx_http_empty_handler;

    c->write->handler = ngx_http_empty_handler;

    sc->last_out = NULL;

    sc->blocked = 1;

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    size = ngx_http_spdy_streams_index_size(sscf);

    for (i = 0; i < size; i++) {

        stream = sc->streams_index[i];

        while (stream) {

            stream->handled = 0;

            r = stream->request;

            fc = r->connection;

            fc->error = 1;

            if (stream->queued) {

                stream->queued = 0;

                ev = fc->write;

                ev->delayed = 0;

            } else {

                ev = fc->read;

            }

            stream = stream->index;

            ev->eof = 1;

            ev->handler(ev);

        }

    }

    sc->blocked = 0;

    if (sc->processing) {

        return;

    }

    ngx_http_close_connection(c);

}

static ngx_int_t

‌ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta)

{

    ngx_uint_t                 i, size;

    ngx_event_t               *wev;

    ngx_http_spdy_stream_t    *stream, *sn;

    ngx_http_spdy_srv_conf_t  *sscf;

    sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,

                                        ngx_http_spdy_module);

    size = ngx_http_spdy_streams_index_size(sscf);

    for (i = 0; i < size; i++) {

        for (stream = sc->streams_index[i]; stream; stream = sn) {

            sn = stream->index;

            if (delta > 0

                && stream->send_window

                      > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta))

            {

                if (ngx_http_spdy_terminate_stream(sc, stream,

                                                   NGX_SPDY_FLOW_CONTROL_ERROR)

                    == NGX_ERROR)

                {

                    return NGX_ERROR;

                }

                continue;

            }

            stream->send_window += delta;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                           "spdy:%ui adjust window:%z",

                           stream->id, stream->send_window);

            if (stream->send_window > 0 && stream->exhausted) {

                stream->exhausted = 0;

                wev = stream->request->connection->write;

                if (!wev->timer_set) {

                    wev->delayed = 0;

                    wev->handler(wev);

                }

            }

        }

    }

    return NGX_OK;

}

static void

‌ngx_http_spdy_pool_cleanup(void *data)

{

    ngx_http_spdy_connection_t  *sc = data;

    if (sc->pool) {

        ngx_destroy_pool(sc->pool);

    }

}

static void *

‌ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size)

{

    ngx_http_spdy_connection_t *sc = opaque;

    return ngx_palloc(sc->connection->pool, items * size);

}

static void

‌ngx_http_spdy_zfree(void *opaque, void *address)

{

#if 0

    ngx_http_spdy_connection_t *sc = opaque;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy zfree: %p", address);

#endif

}

src/http/ngx_http_spdy_module.c - nginx-1.7.9

Global variables defined

ngx_http_spdy_chunk_size_post

ngx_http_spdy_commands

ngx_http_spdy_headers_comp_bounds

ngx_http_spdy_module

ngx_http_spdy_module_ctx

ngx_http_spdy_pool_size_post

ngx_http_spdy_recv_buffer_size_post

ngx_http_spdy_streams_index_mask_post

ngx_http_spdy_vars

Functions defined

ngx_http_spdy_add_variables

ngx_http_spdy_chunk_size

ngx_http_spdy_create_loc_conf

ngx_http_spdy_create_main_conf

ngx_http_spdy_create_srv_conf

ngx_http_spdy_init_main_conf

ngx_http_spdy_merge_loc_conf

ngx_http_spdy_merge_srv_conf

ngx_http_spdy_module_init

ngx_http_spdy_pool_size

ngx_http_spdy_recv_buffer_size

ngx_http_spdy_request_priority_variable

ngx_http_spdy_streams_index_mask

ngx_http_spdy_variable

Source code

/*

* Copyright (C) Nginx, Inc.

* Copyright (C) Valentin V. Bartenev

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_http_spdy_module.h>

static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);

static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);

static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);

static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);

static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);

static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,

    void *child);

static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,

    void *data);

static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,

    void *data);

static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data);

‌static ngx_conf_num_bounds_t  ngx_http_spdy_headers_comp_bounds = {

    ngx_conf_check_num_bounds, 0, 9

};

‌static ngx_conf_post_t  ngx_http_spdy_recv_buffer_size_post =

    { ngx_http_spdy_recv_buffer_size };

‌static ngx_conf_post_t  ngx_http_spdy_pool_size_post =

    { ngx_http_spdy_pool_size };

‌static ngx_conf_post_t  ngx_http_spdy_streams_index_mask_post =

    { ngx_http_spdy_streams_index_mask };

‌static ngx_conf_post_t  ngx_http_spdy_chunk_size_post =

    { ngx_http_spdy_chunk_size };

‌static ngx_command_t  ngx_http_spdy_commands[] = {

    { ngx_string("spdy_recv_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),

      &ngx_http_spdy_recv_buffer_size_post },

    { ngx_string("spdy_pool_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, pool_size),

      &ngx_http_spdy_pool_size_post },

    { ngx_string("spdy_max_concurrent_streams"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),

      NULL },

    { ngx_string("spdy_streams_index_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),

      &ngx_http_spdy_streams_index_mask_post },

    { ngx_string("spdy_recv_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),

      NULL },

    { ngx_string("spdy_keepalive_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),

      NULL },

    { ngx_string("spdy_headers_comp"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_spdy_srv_conf_t, headers_comp),

      &ngx_http_spdy_headers_comp_bounds },

    { ngx_string("spdy_chunk_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_spdy_loc_conf_t, chunk_size),

      &ngx_http_spdy_chunk_size_post },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_spdy_module_ctx = {

    ngx_http_spdy_add_variables,           /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_spdy_create_main_conf,        /* create main configuration */

    ngx_http_spdy_init_main_conf,          /* init main configuration */

    ngx_http_spdy_create_srv_conf,         /* create server configuration */

    ngx_http_spdy_merge_srv_conf,          /* merge server configuration */

    ngx_http_spdy_create_loc_conf,         /* create location configuration */

    ngx_http_spdy_merge_loc_conf           /* merge location configuration */

};

‌ngx_module_t  ngx_http_spdy_module = {

    NGX_MODULE_V1,

    &ngx_http_spdy_module_ctx,             /* module context */

    ngx_http_spdy_commands,                /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    ngx_http_spdy_module_init,             /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_variable_t  ngx_http_spdy_vars[] = {

    { ngx_string("spdy"), NULL,

      ngx_http_spdy_variable, 0, 0, 0 },

    { ngx_string("spdy_request_priority"), NULL,

      ngx_http_spdy_request_priority_variable, 0, 0, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

static ngx_int_t

‌ngx_http_spdy_add_variables(ngx_conf_t *cf)

{

   ngx_http_variable_t  *var, *v;

    for (v = ngx_http_spdy_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->spdy_stream) {

        v->len = sizeof("3.1") - 1;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) "3.1";

        return NGX_OK;

    }

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->spdy_stream) {

        v->len = 1;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = ngx_pnalloc(r->pool, 1);

        if (v->data == NULL) {

            return NGX_ERROR;

        }

        v->data[0] = '0' + (u_char) r->spdy_stream->priority;

        return NGX_OK;

    }

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_module_init(ngx_cycle_t *cycle)

{

    ngx_http_spdy_request_headers_init();

    return NGX_OK;

}

static void *

‌ngx_http_spdy_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_spdy_main_conf_t  *smcf;

    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));

    if (smcf == NULL) {

        return NULL;

    }

    smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;

    return smcf;

}

static char *

‌ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_spdy_main_conf_t *smcf = conf;

    ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024);

    return NGX_CONF_OK;

}

static void *

‌ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)

{

    ngx_http_spdy_srv_conf_t  *sscf;

    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));

    if (sscf == NULL) {

        return NULL;

    }

    sscf->pool_size = NGX_CONF_UNSET_SIZE;

    sscf->concurrent_streams = NGX_CONF_UNSET_UINT;

    sscf->streams_index_mask = NGX_CONF_UNSET_UINT;

    sscf->recv_timeout = NGX_CONF_UNSET_MSEC;

    sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;

    sscf->headers_comp = NGX_CONF_UNSET;

    return sscf;

}

static char *

‌ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_spdy_srv_conf_t *prev = parent;

    ngx_http_spdy_srv_conf_t *conf = child;

    ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);

    ngx_conf_merge_uint_value(conf->concurrent_streams,

                              prev->concurrent_streams, 100);

    ngx_conf_merge_uint_value(conf->streams_index_mask,

                              prev->streams_index_mask, 32 - 1);

    ngx_conf_merge_msec_value(conf->recv_timeout,

                              prev->recv_timeout, 30000);

    ngx_conf_merge_msec_value(conf->keepalive_timeout,

                              prev->keepalive_timeout, 180000);

    ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);

    return NGX_CONF_OK;

}

static void *

‌ngx_http_spdy_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_spdy_loc_conf_t  *slcf;

    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t));

    if (slcf == NULL) {

        return NULL;

    }

    slcf->chunk_size = NGX_CONF_UNSET_SIZE;

    return slcf;

}

static char *

‌ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_spdy_loc_conf_t *prev = parent;

    ngx_http_spdy_loc_conf_t *conf = child;

    ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)

{

    size_t *sp = data;

    if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {

        return "value is too small";

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)

{

    size_t *sp = data;

    if (*sp < NGX_MIN_POOL_SIZE) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the pool size must be no less than %uz",

                           NGX_MIN_POOL_SIZE);

        return NGX_CONF_ERROR;

    }

    if (*sp % NGX_POOL_ALIGNMENT) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the pool size must be a multiple of %uz",

                           NGX_POOL_ALIGNMENT);

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)

{

    ngx_uint_t *np = data;

    ngx_uint_t  mask;

    mask = *np - 1;

    if (*np == 0 || (*np & mask)) {

        return "must be a power of two";

    }

    *np = mask;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data)

{

    size_t *sp = data;

    if (*sp == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the spdy chunk size cannot be zero");

        return NGX_CONF_ERROR;

    }

    if (*sp > NGX_SPDY_MAX_FRAME_SIZE) {

        *sp = NGX_SPDY_MAX_FRAME_SIZE;

    }

    return NGX_CONF_OK;

}

src/http/ngx_http_variables.h - nginx-1.7.9

Data types defined

ngx_http_get_variable_pt

ngx_http_map_regex_t

ngx_http_map_t

ngx_http_regex_t

ngx_http_regex_variable_t

ngx_http_set_variable_pt

ngx_http_variable_s

ngx_http_variable_t

ngx_http_variable_value_t

Macros defined

NGX_HTTP_VAR_CHANGEABLE

NGX_HTTP_VAR_INDEXED

NGX_HTTP_VAR_NOCACHEABLE

NGX_HTTP_VAR_NOHASH

_NGX_HTTP_VARIABLES_H_INCLUDED_

ngx_http_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_

‌#define _NGX_HTTP_VARIABLES_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌typedef ngx_variable_value_t  ngx_http_variable_value_t;

‌#define ngx_http_variable(v)     { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }

‌typedef struct ngx_http_variable_s  ngx_http_variable_t;

‌typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

‌typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

‌#define NGX_HTTP_VAR_CHANGEABLE   1

‌#define NGX_HTTP_VAR_NOCACHEABLE  2

‌#define NGX_HTTP_VAR_INDEXED      4

‌#define NGX_HTTP_VAR_NOHASH       8

‌struct ngx_http_variable_s {

    ngx_str_t                     name;   /* must be first to build the hash */

    ngx_http_set_variable_pt      set_handler;

    ngx_http_get_variable_pt      get_handler;

    uintptr_t                     data;

    ngx_uint_t                    flags;

    ngx_uint_t                    index;

};

ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,

    ngx_uint_t flags);

ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);

ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,

    ngx_uint_t index);

ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,

    ngx_uint_t index);

ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,

    ngx_str_t *name, ngx_uint_t key);

ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,

    ngx_str_t *var, ngx_list_part_t *part, size_t prefix);

#if (NGX_PCRE)

typedef struct {

    ngx_uint_t                    capture;

    ngx_int_t                     index;

‌} ngx_http_regex_variable_t;

typedef struct {

    ngx_regex_t                  *regex;

    ngx_uint_t                    ncaptures;

    ngx_http_regex_variable_t    *variables;

    ngx_uint_t                    nvariables;

    ngx_str_t                     name;

‌} ngx_http_regex_t;

typedef struct {

    ngx_http_regex_t             *regex;

    void                         *value;

‌} ngx_http_map_regex_t;

ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,

    ngx_regex_compile_t *rc);

ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,

    ngx_str_t *s);

#endif

typedef struct {

    ngx_hash_combined_t           hash;

#if (NGX_PCRE)

    ngx_http_map_regex_t         *regex;

    ngx_uint_t                    nregex;

#endif

‌} ngx_http_map_t;

void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,

    ngx_str_t *match);

ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);

ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);

extern ngx_http_variable_value_t  ngx_http_variable_null_value;

extern ngx_http_variable_value_t  ngx_http_variable_true_value;

#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */

src/http/ngx_http_variables.c - nginx-1.7.9

Global variables defined

ngx_http_core_variables

ngx_http_variable_null_value

ngx_http_variable_true_value

Functions defined

ngx_http_add_variable

ngx_http_get_flushed_variable

ngx_http_get_indexed_variable

ngx_http_get_variable

ngx_http_get_variable_index

ngx_http_map_find

ngx_http_regex_compile

ngx_http_regex_exec

ngx_http_variable_argument

ngx_http_variable_binary_remote_addr

ngx_http_variable_body_bytes_sent

ngx_http_variable_bytes_sent

ngx_http_variable_connection

ngx_http_variable_connection_requests

ngx_http_variable_content_length

ngx_http_variable_cookie

ngx_http_variable_cookies

ngx_http_variable_document_root

ngx_http_variable_header

ngx_http_variable_headers

ngx_http_variable_headers_internal

ngx_http_variable_host

ngx_http_variable_hostname

ngx_http_variable_https

ngx_http_variable_is_args

ngx_http_variable_msec

ngx_http_variable_nginx_version

ngx_http_variable_not_found

ngx_http_variable_pid

ngx_http_variable_pipe

ngx_http_variable_proxy_protocol_addr

ngx_http_variable_realpath_root

ngx_http_variable_remote_addr

ngx_http_variable_remote_port

ngx_http_variable_remote_user

ngx_http_variable_request

ngx_http_variable_request_body

ngx_http_variable_request_body_file

ngx_http_variable_request_completion

ngx_http_variable_request_filename

ngx_http_variable_request_get_size

ngx_http_variable_request_length

ngx_http_variable_request_line

ngx_http_variable_request_method

ngx_http_variable_request_set_size

ngx_http_variable_request_time

ngx_http_variable_scheme

ngx_http_variable_sent_connection

ngx_http_variable_sent_content_length

ngx_http_variable_sent_content_type

ngx_http_variable_sent_keep_alive

ngx_http_variable_sent_last_modified

ngx_http_variable_sent_location

ngx_http_variable_sent_transfer_encoding

ngx_http_variable_server_addr

ngx_http_variable_server_name

ngx_http_variable_server_port

ngx_http_variable_set_args

ngx_http_variable_status

ngx_http_variable_tcpinfo

ngx_http_variable_time_iso8601

ngx_http_variable_time_local

ngx_http_variable_unknown_header

ngx_http_variable_unknown_header_in

ngx_http_variable_unknown_header_out

ngx_http_variables_add_core_vars

ngx_http_variables_init_vars

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <nginx.h>

static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

#if 0

static void ngx_http_variable_request_set(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

#endif

static ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static void ngx_http_variable_request_set_size(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data, u_char sep);

static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

#if (NGX_HAVE_TCP_INFO)

static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

#endif

static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static void ngx_http_variable_set_args(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

/*

* TODO:

*     Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED

*                 REMOTE_HOST (null), REMOTE_IDENT (null),

*                 SERVER_SOFTWARE

*

*     Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)

*/

/*

* the $http_host, $http_user_agent, $http_referer, and $http_via

* variables may be handled by generic

* ngx_http_variable_unknown_header_in(), but for performance reasons

* they are handled using dedicated entries

*/

‌static ngx_http_variable_t  ngx_http_core_variables[] = {

    { ngx_string("http_host"), NULL, ngx_http_variable_header,

      offsetof(ngx_http_request_t, headers_in.host), 0, 0 },

    { ngx_string("http_user_agent"), NULL, ngx_http_variable_header,

      offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },

    { ngx_string("http_referer"), NULL, ngx_http_variable_header,

      offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },

#if (NGX_HTTP_GZIP)

    { ngx_string("http_via"), NULL, ngx_http_variable_header,

      offsetof(ngx_http_request_t, headers_in.via), 0, 0 },

#endif

#if (NGX_HTTP_X_FORWARDED_FOR)

    { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_headers,

      offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },

#endif

    { ngx_string("http_cookie"), NULL, ngx_http_variable_cookies,

      offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },

    { ngx_string("content_length"), NULL, ngx_http_variable_content_length,

      0, 0, 0 },

    { ngx_string("content_type"), NULL, ngx_http_variable_header,

      offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },

    { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },

    { ngx_string("binary_remote_addr"), NULL,

      ngx_http_variable_binary_remote_addr, 0, 0, 0 },

    { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },

    { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },

    { ngx_string("proxy_protocol_addr"), NULL,

      ngx_http_variable_proxy_protocol_addr, 0, 0, 0 },

    { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },

    { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },

    { ngx_string("server_protocol"), NULL, ngx_http_variable_request,

      offsetof(ngx_http_request_t, http_protocol), 0, 0 },

    { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },

    { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },

    { ngx_string("request_uri"), NULL, ngx_http_variable_request,

      offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },

    { ngx_string("uri"), NULL, ngx_http_variable_request,

      offsetof(ngx_http_request_t, uri),

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("document_uri"), NULL, ngx_http_variable_request,

      offsetof(ngx_http_request_t, uri),

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 },

    { ngx_string("document_root"), NULL,

      ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("realpath_root"), NULL,

      ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("query_string"), NULL, ngx_http_variable_request,

      offsetof(ngx_http_request_t, args),

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("args"),

      ngx_http_variable_set_args,

      ngx_http_variable_request,

      offsetof(ngx_http_request_t, args),

      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("is_args"), NULL, ngx_http_variable_is_args,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("request_filename"), NULL,

      ngx_http_variable_request_filename, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },

    { ngx_string("request_method"), NULL,

      ngx_http_variable_request_method, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },

    { ngx_string("bytes_sent"), NULL, ngx_http_variable_bytes_sent,

      0, 0, 0 },

    { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,

      0, 0, 0 },

    { ngx_string("pipe"), NULL, ngx_http_variable_pipe,

      0, 0, 0 },

    { ngx_string("request_completion"), NULL,

      ngx_http_variable_request_completion,

      0, 0, 0 },

    { ngx_string("request_body"), NULL,

      ngx_http_variable_request_body,

      0, 0, 0 },

    { ngx_string("request_body_file"), NULL,

      ngx_http_variable_request_body_file,

      0, 0, 0 },

    { ngx_string("request_length"), NULL, ngx_http_variable_request_length,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("request_time"), NULL, ngx_http_variable_request_time,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("status"), NULL,

      ngx_http_variable_status, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("sent_http_content_type"), NULL,

      ngx_http_variable_sent_content_type, 0, 0, 0 },

    { ngx_string("sent_http_content_length"), NULL,

      ngx_http_variable_sent_content_length, 0, 0, 0 },

    { ngx_string("sent_http_location"), NULL,

      ngx_http_variable_sent_location, 0, 0, 0 },

    { ngx_string("sent_http_last_modified"), NULL,

      ngx_http_variable_sent_last_modified, 0, 0, 0 },

    { ngx_string("sent_http_connection"), NULL,

      ngx_http_variable_sent_connection, 0, 0, 0 },

    { ngx_string("sent_http_keep_alive"), NULL,

      ngx_http_variable_sent_keep_alive, 0, 0, 0 },

    { ngx_string("sent_http_transfer_encoding"), NULL,

      ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },

    { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,

      offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },

    { ngx_string("limit_rate"), ngx_http_variable_request_set_size,

      ngx_http_variable_request_get_size,

      offsetof(ngx_http_request_t, limit_rate),

      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("connection"), NULL,

      ngx_http_variable_connection, 0, 0, 0 },

    { ngx_string("connection_requests"), NULL,

      ngx_http_variable_connection_requests, 0, 0, 0 },

    { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,

      0, 0, 0 },

    { ngx_string("hostname"), NULL, ngx_http_variable_hostname,

      0, 0, 0 },

    { ngx_string("pid"), NULL, ngx_http_variable_pid,

      0, 0, 0 },

    { ngx_string("msec"), NULL, ngx_http_variable_msec,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("time_iso8601"), NULL, ngx_http_variable_time_iso8601,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("time_local"), NULL, ngx_http_variable_time_local,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

#if (NGX_HAVE_TCP_INFO)

    { ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo,

      1, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo,

      2, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo,

      3, NGX_HTTP_VAR_NOCACHEABLE, 0 },

#endif

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌ngx_http_variable_value_t  ngx_http_variable_null_value =

    ngx_http_variable("");

‌ngx_http_variable_value_t  ngx_http_variable_true_value =

    ngx_http_variable("1");

ngx_http_variable_t *

‌ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)

{

    ngx_int_t                   rc;

    ngx_uint_t                  i;

    ngx_hash_key_t             *key;

    ngx_http_variable_t        *v;

    ngx_http_core_main_conf_t  *cmcf;

    if (name->len == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"$\"");

        return NULL;

    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    key = cmcf->variables_keys->keys.elts;

    for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {

        if (name->len != key[i].key.len

            || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)

        {

            continue;

        }

        v = key[i].value;

        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "the duplicate \"%V\" variable", name);

            return NULL;

        }

        return v;

    }

    v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));

    if (v == NULL) {

        return NULL;

    }

    v->name.len = name->len;

    v->name.data = ngx_pnalloc(cf->pool, name->len);

    if (v->name.data == NULL) {

        return NULL;

    }

    ngx_strlow(v->name.data, name->data, name->len);

    v->set_handler = NULL;

    v->get_handler = NULL;

    v->data = 0;

    v->flags = flags;

    v->index = 0;

    rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);

    if (rc == NGX_ERROR) {

        return NULL;

    }

    if (rc == NGX_BUSY) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "conflicting variable name \"%V\"", name);

        return NULL;

    }

    return v;

}

ngx_int_t

‌ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)

{

    ngx_uint_t                  i;

    ngx_http_variable_t        *v;

    ngx_http_core_main_conf_t  *cmcf;

    if (name->len == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"$\"");

        return NGX_ERROR;

    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    v = cmcf->variables.elts;

    if (v == NULL) {

        if (ngx_array_init(&cmcf->variables, cf->pool, 4,

                           sizeof(ngx_http_variable_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    } else {

        for (i = 0; i < cmcf->variables.nelts; i++) {

            if (name->len != v[i].name.len

                || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)

            {

                continue;

            }

            return i;

        }

    }

    v = ngx_array_push(&cmcf->variables);

    if (v == NULL) {

        return NGX_ERROR;

    }

    v->name.len = name->len;

    v->name.data = ngx_pnalloc(cf->pool, name->len);

    if (v->name.data == NULL) {

        return NGX_ERROR;

    }

    ngx_strlow(v->name.data, name->data, name->len);

    v->set_handler = NULL;

    v->get_handler = NULL;

    v->data = 0;

    v->flags = 0;

    v->index = cmcf->variables.nelts - 1;

    return v->index;

}

ngx_http_variable_value_t *

‌ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)

{

    ngx_http_variable_t        *v;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    if (cmcf->variables.nelts <= index) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "unknown variable index: %ui", index);

        return NULL;

    }

    if (r->variables[index].not_found || r->variables[index].valid) {

        return &r->variables[index];

    }

    v = cmcf->variables.elts;

    if (v[index].get_handler(r, &r->variables[index], v[index].data)

        == NGX_OK)

    {

        if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {

            r->variables[index].no_cacheable = 1;

        }

        return &r->variables[index];

    }

    r->variables[index].valid = 0;

    r->variables[index].not_found = 1;

    return NULL;

}

ngx_http_variable_value_t *

‌ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)

{

    ngx_http_variable_value_t  *v;

    v = &r->variables[index];

    if (v->valid || v->not_found) {

        if (!v->no_cacheable) {

            return v;

        }

        v->valid = 0;

        v->not_found = 0;

    }

    return ngx_http_get_indexed_variable(r, index);

}

ngx_http_variable_value_t *

‌ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)

{

    ngx_http_variable_t        *v;

    ngx_http_variable_value_t  *vv;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);

    if (v) {

        if (v->flags & NGX_HTTP_VAR_INDEXED) {

            return ngx_http_get_flushed_variable(r, v->index);

        } else {

            vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));

            if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {

                return vv;

            }

            return NULL;

        }

    }

    vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));

    if (vv == NULL) {

        return NULL;

    }

    if (ngx_strncmp(name->data, "http_", 5) == 0) {

        if (ngx_http_variable_unknown_header_in(r, vv, (uintptr_t) name)

            == NGX_OK)

        {

            return vv;

        }

        return NULL;

    }

    if (ngx_strncmp(name->data, "sent_http_", 10) == 0) {

        if (ngx_http_variable_unknown_header_out(r, vv, (uintptr_t) name)

            == NGX_OK)

        {

            return vv;

        }

        return NULL;

    }

    if (ngx_strncmp(name->data, "upstream_http_", 14) == 0) {

        if (ngx_http_upstream_header_variable(r, vv, (uintptr_t) name)

            == NGX_OK)

        {

            return vv;

        }

        return NULL;

    }

    if (ngx_strncmp(name->data, "cookie_", 7) == 0) {

        if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) {

            return vv;

        }

        return NULL;

    }

    if (ngx_strncmp(name->data, "upstream_cookie_", 16) == 0) {

        if (ngx_http_upstream_cookie_variable(r, vv, (uintptr_t) name)

            == NGX_OK)

        {

            return vv;

        }

        return NULL;

    }

    if (ngx_strncmp(name->data, "arg_", 4) == 0) {

        if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {

            return vv;

        }

        return NULL;

    }

    vv->not_found = 1;

    return vv;

}

static ngx_int_t

‌ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_str_t  *s;

    s = (ngx_str_t *) ((char *) r + data);

    if (s->data) {

        v->len = s->len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = s->data;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

#if 0

static void

ngx_http_variable_request_set(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_str_t  *s;

    s = (ngx_str_t *) ((char *) r + data);

    s->len = v->len;

    s->data = v->data;

}

#endif

static ngx_int_t

‌ngx_http_variable_request_get_size(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t  *sp;

    sp = (size_t *) ((char *) r + data);

    v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(v->data, "%uz", *sp) - v->data;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

static void

‌ngx_http_variable_request_set_size(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ssize_t    s, *sp;

    ngx_str_t  val;

    val.len = v->len;

    val.data = v->data;

    s = ngx_parse_size(&val);

    if (s == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "invalid size \"%V\"", &val);

        return;

    }

    sp = (ssize_t *) ((char *) r + data);

    *sp = s;

    return;

}

static ngx_int_t

‌ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_table_elt_t  *h;

    h = *(ngx_table_elt_t **) ((char *) r + data);

    if (h) {

        v->len = h->value.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = h->value.data;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_cookies(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    return ngx_http_variable_headers_internal(r, v, data, ';');

}

static ngx_int_t

‌ngx_http_variable_headers(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    return ngx_http_variable_headers_internal(r, v, data, ',');

}

static ngx_int_t

‌ngx_http_variable_headers_internal(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data, u_char sep)

{

    size_t             len;

    u_char            *p, *end;

    ngx_uint_t         i, n;

    ngx_array_t       *a;

    ngx_table_elt_t  **h;

    a = (ngx_array_t *) ((char *) r + data);

    n = a->nelts;

    h = a->elts;

    len = 0;

    for (i = 0; i < n; i++) {

        if (h[i]->hash == 0) {

            continue;

        }

        len += h[i]->value.len + 2;

    }

    if (len == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    len -= 2;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (n == 1) {

        v->len = (*h)->value.len;

        v->data = (*h)->value.data;

        return NGX_OK;

    }

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = len;

    v->data = p;

    end = p + len;

    for (i = 0; /* void */ ; i++) {

        if (h[i]->hash == 0) {

            continue;

        }

        p = ngx_copy(p, h[i]->value.data, h[i]->value.len);

        if (p == end) {

            break;

        }

        *p++ = sep; *p++ = ' ';

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_unknown_header_in(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,

                                            &r->headers_in.headers.part,

                                            sizeof("http_") - 1);

}

static ngx_int_t

‌ngx_http_variable_unknown_header_out(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,

                                            &r->headers_out.headers.part,

                                            sizeof("sent_http_") - 1);

}

ngx_int_t

‌ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,

    ngx_list_part_t *part, size_t prefix)

{

    u_char            ch;

    ngx_uint_t        i, n;

    ngx_table_elt_t  *header;

    header = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0) {

            continue;

        }

        for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {

            ch = header[i].key.data[n];

            if (ch >= 'A' && ch <= 'Z') {

                ch |= 0x20;

            } else if (ch == '-') {

                ch = '_';

            }

            if (var->data[n + prefix] != ch) {

                break;

            }

        }

        if (n + prefix == var->len && n == header[i].key.len) {

            v->len = header[i].value.len;

            v->valid = 1;

            v->no_cacheable = 0;

            v->not_found = 0;

            v->data = header[i].value.data;

            return NGX_OK;

        }

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_line(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p, *s;

    s = r->request_line.data;

    if (s == NULL) {

        s = r->request_start;

        if (s == NULL) {

            v->not_found = 1;

            return NGX_OK;

        }

        for (p = s; p < r->header_in->last; p++) {

            if (*p == CR || *p == LF) {

                break;

            }

        }

        r->request_line.len = p - s;

        r->request_line.data = s;

    }

    v->len = r->request_line.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = s;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_str_t *name = (ngx_str_t *) data;

    ngx_str_t  cookie, s;

    s.len = name->len - (sizeof("cookie_") - 1);

    s.data = name->data + sizeof("cookie_") - 1;

    if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)

        == NGX_DECLINED)

    {

        v->not_found = 1;

        return NGX_OK;

    }

    v->len = cookie.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = cookie.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_str_t *name = (ngx_str_t *) data;

    u_char     *arg;

    size_t      len;

    ngx_str_t   value;

    len = name->len - (sizeof("arg_") - 1);

    arg = name->data + sizeof("arg_") - 1;

    if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->data = value.data;

    v->len = value.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

#if (NGX_HAVE_TCP_INFO)

static ngx_int_t

‌ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    struct tcp_info  ti;

    socklen_t        len;

    uint32_t         value;

    len = sizeof(struct tcp_info);

    if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    switch (data) {

    case 0:

        value = ti.tcpi_rtt;

        break;

    case 1:

        value = ti.tcpi_rttvar;

        break;

    case 2:

        value = ti.tcpi_snd_cwnd;

        break;

    case 3:

        value = ti.tcpi_rcv_space;

        break;

    /* suppress warning */

    default:

        value = 0;

        break;

    }

    v->len = ngx_sprintf(v->data, "%uD", value) - v->data;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_variable_content_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    if (r->headers_in.content_length) {

        v->len = r->headers_in.content_length->value.len;

        v->data = r->headers_in.content_length->value.data;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

    } else if (r->headers_in.content_length_n >= 0) {

        p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

        if (p == NULL) {

            return NGX_ERROR;

        }

        v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p;

        v->data = p;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_core_srv_conf_t  *cscf;

    if (r->headers_in.server.len) {

        v->len = r->headers_in.server.len;

        v->data = r->headers_in.server.data;

    } else {

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        v->len = cscf->server_name.len;

        v->data = cscf->server_name.data;

    }

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    switch (r->connection->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;

        v->len = sizeof(struct in6_addr);

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = sin6->sin6_addr.s6_addr;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) r->connection->sockaddr;

        v->len = sizeof(in_addr_t);

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) &sin->sin_addr;

        break;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_remote_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->len = r->connection->addr_text.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = r->connection->addr_text.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_remote_port(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_uint_t            port;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    v->len = 0;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    switch (r->connection->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;

        port = ntohs(sin6->sin6_port);

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        port = 0;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) r->connection->sockaddr;

        port = ntohs(sin->sin_port);

        break;

    }

    if (port > 0 && port < 65536) {

        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->len = r->connection->proxy_protocol_addr.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = r->connection->proxy_protocol_addr.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_server_addr(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_str_t  s;

    u_char     addr[NGX_SOCKADDR_STRLEN];

    s.len = NGX_SOCKADDR_STRLEN;

    s.data = addr;

    if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {

        return NGX_ERROR;

    }

    s.data = ngx_pnalloc(r->pool, s.len);

    if (s.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s.data, addr, s.len);

    v->len = s.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = s.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_server_port(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_uint_t            port;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    v->len = 0;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {

        return NGX_ERROR;

    }

    v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    switch (r->connection->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;

        port = ntohs(sin6->sin6_port);

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        port = 0;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) r->connection->local_sockaddr;

        port = ntohs(sin->sin_port);

        break;

    }

    if (port > 0 && port < 65536) {

        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_scheme(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

#if (NGX_HTTP_SSL)

    if (r->connection->ssl) {

        v->len = sizeof("https") - 1;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) "https";

        return NGX_OK;

    }

#endif

    v->len = sizeof("http") - 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = (u_char *) "http";

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_https(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

#if (NGX_HTTP_SSL)

    if (r->connection->ssl) {

        v->len = sizeof("on") - 1;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) "on";

        return NGX_OK;

    }

#endif

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static void

‌ngx_http_variable_set_args(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    r->args.len = v->len;

    r->args.data = v->data;

    r->valid_unparsed_uri = 0;

}

static ngx_int_t

‌ngx_http_variable_is_args(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (r->args.len == 0) {

        v->len = 0;

        v->data = NULL;

        return NGX_OK;

    }

    v->len = 1;

    v->data = (u_char *) "?";

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_document_root(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_str_t                  path;

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->root_lengths == NULL) {

        v->len = clcf->root.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = clcf->root.data;

    } else {

        if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0,

                                clcf->root_values->elts)

            == NULL)

        {

            return NGX_ERROR;

        }

        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        v->len = path.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = path.data;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_realpath_root(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                    *real;

    size_t                     len;

    ngx_str_t                  path;

    ngx_http_core_loc_conf_t  *clcf;

#if (NGX_HAVE_MAX_PATH)

    u_char                     buffer[NGX_MAX_PATH];

#endif

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->root_lengths == NULL) {

        path = clcf->root;

    } else {

        if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1,

                                clcf->root_values->elts)

            == NULL)

        {

            return NGX_ERROR;

        }

        path.data[path.len - 1] = '\0';

        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

#if (NGX_HAVE_MAX_PATH)

    real = buffer;

#else

    real = NULL;

#endif

    real = ngx_realpath(path.data, real);

    if (real == NULL) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                      ngx_realpath_n " \"%s\" failed", path.data);

        return NGX_ERROR;

    }

    len = ngx_strlen(real);

    v->data = ngx_pnalloc(r->pool, len);

    if (v->data == NULL) {

#if !(NGX_HAVE_MAX_PATH)

        ngx_free(real);

#endif

        return NGX_ERROR;

    }

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    ngx_memcpy(v->data, real, len);

#if !(NGX_HAVE_MAX_PATH)

    ngx_free(real);

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_filename(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t     root;

    ngx_str_t  path;

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

        return NGX_ERROR;

    }

    /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */

    v->len = path.len - 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = path.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_server_name(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_core_srv_conf_t  *cscf;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    v->len = cscf->server_name.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = cscf->server_name.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_method(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->main->method_name.data) {

        v->len = r->main->method_name.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = r->main->method_name.data;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_remote_user(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_int_t  rc;

    rc = ngx_http_auth_basic_user(r);

    if (rc == NGX_DECLINED) {

        v->not_found = 1;

        return NGX_OK;

    }

    if (rc == NGX_ERROR) {

        return NGX_ERROR;

    }

    v->len = r->headers_in.user.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = r->headers_in.user.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_bytes_sent(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%O", r->connection->sent) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    off_t    sent;

    u_char  *p;

    sent = r->connection->sent - r->header_size;

    if (sent < 0) {

        sent = 0;

    }

    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%O", sent) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_pipe(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->data = (u_char *) (r->pipeline ? "p" : ".");

    v->len = 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_status(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_uint_t  status;

    v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    if (r->err_status) {

        status = r->err_status;

    } else if (r->headers_out.status) {

        status = r->headers_out.status;

    } else if (r->http_version == NGX_HTTP_VERSION_9) {

        status = 9;

    } else {

        status = 0;

    }

    v->len = ngx_sprintf(v->data, "%03ui", status) - v->data;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_content_type(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->headers_out.content_type.len) {

        v->len = r->headers_out.content_type.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = r->headers_out.content_type.data;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_content_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    if (r->headers_out.content_length) {

        v->len = r->headers_out.content_length->value.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = r->headers_out.content_length->value.data;

        return NGX_OK;

    }

    if (r->headers_out.content_length_n >= 0) {

        p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

        if (p == NULL) {

            return NGX_ERROR;

        }

        v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = p;

        return NGX_OK;

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_location(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_str_t  name;

    if (r->headers_out.location) {

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

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

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

        return NGX_OK;

    }

    ngx_str_set(&name, "sent_http_location");

    return ngx_http_variable_unknown_header(v, &name,

                                            &r->headers_out.headers.part,

                                            sizeof("sent_http_") - 1);

}

static ngx_int_t

‌ngx_http_variable_sent_last_modified(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    if (r->headers_out.last_modified) {

        v->len = r->headers_out.last_modified->value.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = r->headers_out.last_modified->value.data;

        return NGX_OK;

    }

    if (r->headers_out.last_modified_time >= 0) {

        p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);

        if (p == NULL) {

            return NGX_ERROR;

        }

        v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = p;

        return NGX_OK;

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_connection(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t   len;

    char    *p;

    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {

        len = sizeof("upgrade") - 1;

        p = "upgrade";

    } else if (r->keepalive) {

        len = sizeof("keep-alive") - 1;

        p = "keep-alive";

    } else {

        len = sizeof("close") - 1;

        p = "close";

    }

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = (u_char *) p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                    *p;

    ngx_http_core_loc_conf_t  *clcf;

    if (r->keepalive) {

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (clcf->keepalive_header) {

            p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);

            if (p == NULL) {

                return NGX_ERROR;

            }

            v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;

            v->valid = 1;

            v->no_cacheable = 0;

            v->not_found = 0;

            v->data = p;

            return NGX_OK;

        }

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->chunked) {

        v->len = sizeof("chunked") - 1;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) "chunked";

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_completion(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->request_complete) {

        v->len = 2;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = (u_char *) "OK";

        return NGX_OK;

    }

    v->len = 0;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = (u_char *) "";

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_body(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char       *p;

    size_t        len;

    ngx_buf_t    *buf;

    ngx_chain_t  *cl;

    if (r->request_body == NULL

        || r->request_body->bufs == NULL

        || r->request_body->temp_file)

    {

        v->not_found = 1;

        return NGX_OK;

    }

    cl = r->request_body->bufs;

    buf = cl->buf;

    if (cl->next == NULL) {

        v->len = buf->last - buf->pos;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = buf->pos;

        return NGX_OK;

    }

    len = buf->last - buf->pos;

    cl = cl->next;

    for ( /* void */ ; cl; cl = cl->next) {

        buf = cl->buf;

        len += buf->last - buf->pos;

    }

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->data = p;

    cl = r->request_body->bufs;

    for ( /* void */ ; cl; cl = cl->next) {

        buf = cl->buf;

        p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);

    }

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_body_file(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->request_body == NULL || r->request_body->temp_file == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->len = r->request_body->temp_file->file.name.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = r->request_body->temp_file->file.name.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_length(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%O", r->request_length) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_request_time(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char          *p;

    ngx_time_t      *tp;

    ngx_msec_int_t   ms;

    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);

    if (p == NULL) {

        return NGX_ERROR;

    }

    tp = ngx_timeofday();

    ms = (ngx_msec_int_t)

             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));

    ms = ngx_max(ms, 0);

    v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_connection(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%uA", r->connection->number) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_connection_requests(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, NGX_INT_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%ui", r->connection->requests) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_nginx_version(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->len = sizeof(NGINX_VERSION) - 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = (u_char *) NGINX_VERSION;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_hostname(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    v->len = ngx_cycle->hostname.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = ngx_cycle->hostname.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_pid(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, NGX_INT64_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(p, "%P", ngx_pid) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_msec(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char      *p;

    ngx_time_t  *tp;

    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);

    if (p == NULL) {

        return NGX_ERROR;

    }

    tp = ngx_timeofday();

    v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_time_iso8601(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(p, ngx_cached_http_log_iso8601.data,

               ngx_cached_http_log_iso8601.len);

    v->len = ngx_cached_http_log_iso8601.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_variable_time_local(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);

    v->len = ngx_cached_http_log_time.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

void *

‌ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match)

{

    void        *value;

    u_char      *low;

    size_t       len;

    ngx_uint_t   key;

    len = match->len;

    if (len) {

        low = ngx_pnalloc(r->pool, len);

        if (low == NULL) {

            return NULL;

        }

    } else {

        low = NULL;

    }

    key = ngx_hash_strlow(low, match->data, len);

    value = ngx_hash_find_combined(&map->hash, key, low, len);

    if (value) {

        return value;

    }

#if (NGX_PCRE)

    if (len && map->nregex) {

        ngx_int_t              n;

        ngx_uint_t             i;

        ngx_http_map_regex_t  *reg;

        reg = map->regex;

        for (i = 0; i < map->nregex; i++) {

            n = ngx_http_regex_exec(r, reg[i].regex, match);

            if (n == NGX_OK) {

                return reg[i].value;

            }

            if (n == NGX_DECLINED) {

                continue;

            }

            /* NGX_ERROR */

            return NULL;

        }

    }

#endif

    return NULL;

}

#if (NGX_PCRE)

static ngx_int_t

‌ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    v->not_found = 1;

    return NGX_OK;

}

ngx_http_regex_t *

‌ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)

{

    u_char                     *p;

    size_t                      size;

    ngx_str_t                   name;

    ngx_uint_t                  i, n;

    ngx_http_variable_t        *v;

    ngx_http_regex_t           *re;

    ngx_http_regex_variable_t  *rv;

    ngx_http_core_main_conf_t  *cmcf;

    rc->pool = cf->pool;

    if (ngx_regex_compile(rc) != NGX_OK) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);

        return NULL;

    }

    re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));

    if (re == NULL) {

        return NULL;

    }

    re->regex = rc->regex;

    re->ncaptures = rc->captures;

    re->name = rc->pattern;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);

    n = (ngx_uint_t) rc->named_captures;

    if (n == 0) {

        return re;

    }

    rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));

    if (rv == NULL) {

        return NULL;

    }

    re->variables = rv;

    re->nvariables = n;

    size = rc->name_size;

    p = rc->names;

    for (i = 0; i < n; i++) {

        rv[i].capture = 2 * ((p[0] << 8) + p[1]);

        name.data = &p[2];

        name.len = ngx_strlen(name.data);

        v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);

        if (v == NULL) {

            return NULL;

        }

        rv[i].index = ngx_http_get_variable_index(cf, &name);

        if (rv[i].index == NGX_ERROR) {

            return NULL;

        }

        v->get_handler = ngx_http_variable_not_found;

        p += size;

    }

    return re;

}

ngx_int_t

‌ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)

{

    ngx_int_t                   rc, index;

    ngx_uint_t                  i, n, len;

    ngx_http_variable_value_t  *vv;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    if (re->ncaptures) {

        len = cmcf->ncaptures;

        if (r->captures == NULL) {

            r->captures = ngx_palloc(r->pool, len * sizeof(int));

            if (r->captures == NULL) {

                return NGX_ERROR;

            }

        }

    } else {

        len = 0;

    }

    rc = ngx_regex_exec(re->regex, s, r->captures, len);

    if (rc == NGX_REGEX_NO_MATCHED) {

        return NGX_DECLINED;

    }

    if (rc < 0) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",

                      rc, s, &re->name);

        return NGX_ERROR;

    }

    for (i = 0; i < re->nvariables; i++) {

        n = re->variables[i].capture;

        index = re->variables[i].index;

        vv = &r->variables[index];

        vv->len = r->captures[n + 1] - r->captures[n];

        vv->valid = 1;

        vv->no_cacheable = 0;

        vv->not_found = 0;

        vv->data = &s->data[r->captures[n]];

#if (NGX_DEBUG)

        {

        ngx_http_variable_t  *v;

        v = cmcf->variables.elts;

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http regex set $%V to \"%*s\"",

                       &v[index].name, vv->len, vv->data);

        }

#endif

    }

    r->ncaptures = rc * 2;

    r->captures_data = s->data;

    return NGX_OK;

}

#endif

ngx_int_t

‌ngx_http_variables_add_core_vars(ngx_conf_t *cf)

{

    ngx_int_t                   rc;

    ngx_http_variable_t        *cv, *v;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,

                                       sizeof(ngx_hash_keys_arrays_t));

    if (cmcf->variables_keys == NULL) {

        return NGX_ERROR;

    }

    cmcf->variables_keys->pool = cf->pool;

    cmcf->variables_keys->temp_pool = cf->pool;

    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    for (cv = ngx_http_core_variables; cv->name.len; cv++) {

        v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));

        if (v == NULL) {

            return NGX_ERROR;

        }

        *v = *cv;

        rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,

                              NGX_HASH_READONLY_KEY);

        if (rc == NGX_OK) {

            continue;

        }

        if (rc == NGX_BUSY) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "conflicting variable name \"%V\"", &v->name);

        }

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_http_variables_init_vars(ngx_conf_t *cf)

{

    ngx_uint_t                  i, n;

    ngx_hash_key_t             *key;

    ngx_hash_init_t             hash;

    ngx_http_variable_t        *v, *av;

    ngx_http_core_main_conf_t  *cmcf;

    /* set the handlers for the indexed http variables */

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    v = cmcf->variables.elts;

    key = cmcf->variables_keys->keys.elts;

    for (i = 0; i < cmcf->variables.nelts; i++) {

        for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {

            av = key[n].value;

            if (v[i].name.len == key[n].key.len

                && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)

                   == 0)

            {

                v[i].get_handler = av->get_handler;

                v[i].data = av->data;

                av->flags |= NGX_HTTP_VAR_INDEXED;

                v[i].flags = av->flags;

                av->index = i;

                if (av->get_handler == NULL) {

                    break;

                }

                goto next;

            }

        }

        if (ngx_strncmp(v[i].name.data, "http_", 5) == 0) {

            v[i].get_handler = ngx_http_variable_unknown_header_in;

            v[i].data = (uintptr_t) &v[i].name;

            continue;

        }

        if (ngx_strncmp(v[i].name.data, "sent_http_", 10) == 0) {

            v[i].get_handler = ngx_http_variable_unknown_header_out;

            v[i].data = (uintptr_t) &v[i].name;

            continue;

        }

        if (ngx_strncmp(v[i].name.data, "upstream_http_", 14) == 0) {

            v[i].get_handler = ngx_http_upstream_header_variable;

            v[i].data = (uintptr_t) &v[i].name;

            v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;

            continue;

        }

        if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) {

            v[i].get_handler = ngx_http_variable_cookie;

            v[i].data = (uintptr_t) &v[i].name;

            continue;

        }

        if (ngx_strncmp(v[i].name.data, "upstream_cookie_", 16) == 0) {

            v[i].get_handler = ngx_http_upstream_cookie_variable;

            v[i].data = (uintptr_t) &v[i].name;

            v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;

            continue;

        }

        if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {

            v[i].get_handler = ngx_http_variable_argument;

            v[i].data = (uintptr_t) &v[i].name;

            v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;

            continue;

        }

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "unknown \"%V\" variable", &v[i].name);

        return NGX_ERROR;

    next:

        continue;

    }

    for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {

        av = key[n].value;

        if (av->flags & NGX_HTTP_VAR_NOHASH) {

            key[n].key.data = NULL;

        }

    }

    hash.hash = &cmcf->variables_hash;

    hash.key = ngx_hash_key;

    hash.max_size = cmcf->variables_hash_max_size;

    hash.bucket_size = cmcf->variables_hash_bucket_size;

    hash.name = "variables_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,

                      cmcf->variables_keys->keys.nelts)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    cmcf->variables_keys = NULL;

    return NGX_OK;

}

src/core/nginx.h - nginx-1.7.9

Macros defined

NGINX_VAR

NGINX_VER

NGINX_VERSION

NGINX_VER_BUILD

NGINX_VER_BUILD

NGX_OLDPID_EXT

_NGINX_H_INCLUDED_

nginx_version

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGINX_H_INCLUDED_

‌#define _NGINX_H_INCLUDED_

‌#define nginx_version      1007009

‌#define NGINX_VERSION      "1.7.9"

‌#define NGINX_VER          "nginx/" NGINX_VERSION

#ifdef NGX_BUILD

‌#define NGINX_VER_BUILD    NGINX_VER " (" NGX_BUILD ")"

#else

‌#define NGINX_VER_BUILD    NGINX_VER

#endif

‌#define NGINX_VAR          "NGINX"

‌#define NGX_OLDPID_EXT     ".oldbin"

#endif /* _NGINX_H_INCLUDED_ */

src/http/ngx_http_config.h - nginx-1.7.9

Data types defined

ngx_http_conf_ctx_t

ngx_http_module_t

Macros defined

NGX_HTTP_LIF_CONF

NGX_HTTP_LMT_CONF

NGX_HTTP_LOC_CONF

NGX_HTTP_LOC_CONF_OFFSET

NGX_HTTP_MAIN_CONF

NGX_HTTP_MAIN_CONF_OFFSET

NGX_HTTP_MODULE

NGX_HTTP_SIF_CONF

NGX_HTTP_SRV_CONF

NGX_HTTP_SRV_CONF_OFFSET

NGX_HTTP_UPS_CONF

_NGX_HTTP_CONFIG_H_INCLUDED_

ngx_http_conf_get_module_loc_conf

ngx_http_conf_get_module_main_conf

ngx_http_conf_get_module_srv_conf

ngx_http_cycle_get_module_main_conf

ngx_http_get_module_loc_conf

ngx_http_get_module_main_conf

ngx_http_get_module_srv_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_

‌#define _NGX_HTTP_CONFIG_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    void        **main_conf;

    void        **srv_conf;

    void        **loc_conf;

‌} ngx_http_conf_ctx_t;

typedef struct {

    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);

    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    void       *(*create_main_conf)(ngx_conf_t *cf);

    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);

    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);

    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);

‌} ngx_http_module_t;

‌#define NGX_HTTP_MODULE           0x50545448   /* "HTTP" */

‌#define NGX_HTTP_MAIN_CONF        0x02000000

‌#define NGX_HTTP_SRV_CONF         0x04000000

‌#define NGX_HTTP_LOC_CONF         0x08000000

‌#define NGX_HTTP_UPS_CONF         0x10000000

‌#define NGX_HTTP_SIF_CONF         0x20000000

‌#define NGX_HTTP_LIF_CONF         0x40000000

‌#define NGX_HTTP_LMT_CONF         0x80000000

‌#define NGX_HTTP_MAIN_CONF_OFFSET  offsetof(ngx_http_conf_ctx_t, main_conf)

‌#define NGX_HTTP_SRV_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, srv_conf)

‌#define NGX_HTTP_LOC_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, loc_conf)

‌#define ngx_http_get_module_main_conf(r, module)                             \

    (r)->main_conf[module.ctx_index]

‌#define ngx_http_get_module_srv_conf(r, module)  (r)->srv_conf[module.ctx_index]

‌#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]

‌#define ngx_http_conf_get_module_main_conf(cf, module)                        \

    ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]

‌#define ngx_http_conf_get_module_srv_conf(cf, module)                         \

    ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]

‌#define ngx_http_conf_get_module_loc_conf(cf, module)                         \

    ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]

‌#define ngx_http_cycle_get_module_main_conf(cycle, module)                    \

    (cycle->conf_ctx[ngx_http_module.index] ?                                 \

        ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index])      \

            ->main_conf[module.ctx_index]:                                    \

        NULL)

#endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */

src/http/ngx_http.c - nginx-1.7.9

Global variables defined

ngx_http_commands

ngx_http_html_default_types

ngx_http_max_module

ngx_http_module

ngx_http_module_ctx

ngx_http_top_body_filter

ngx_http_top_header_filter

Functions defined

ngx_http_add_address

ngx_http_add_addresses

ngx_http_add_addrs

ngx_http_add_addrs6

ngx_http_add_listen

ngx_http_add_listening

ngx_http_add_location

ngx_http_add_server

ngx_http_block

ngx_http_cmp_conf_addrs

ngx_http_cmp_dns_wildcards

ngx_http_cmp_locations

ngx_http_create_locations_list

ngx_http_create_locations_tree

ngx_http_init_headers_in_hash

ngx_http_init_listening

ngx_http_init_locations

ngx_http_init_phase_handlers

ngx_http_init_phases

ngx_http_init_static_location_trees

ngx_http_join_exact_locations

ngx_http_merge_locations

ngx_http_merge_servers

ngx_http_merge_types

ngx_http_optimize_servers

ngx_http_server_names

ngx_http_set_default_types

ngx_http_types_slot

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,

    ngx_http_core_main_conf_t *cmcf);

static ngx_int_t ngx_http_init_headers_in_hash(ngx_conf_t *cf,

    ngx_http_core_main_conf_t *cmcf);

static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf,

    ngx_http_core_main_conf_t *cmcf);

static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf,

    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,

    ngx_http_listen_opt_t *lsopt);

static ngx_int_t ngx_http_add_address(ngx_conf_t *cf,

    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,

    ngx_http_listen_opt_t *lsopt);

static ngx_int_t ngx_http_add_server(ngx_conf_t *cf,

    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr);

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);

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);

static ngx_int_t ngx_http_init_locations(ngx_conf_t *cf,

    ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf);

static ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf,

    ngx_http_core_loc_conf_t *pclcf);

static ngx_int_t ngx_http_cmp_locations(const ngx_queue_t *one,

    const ngx_queue_t *two);

static ngx_int_t ngx_http_join_exact_locations(ngx_conf_t *cf,

    ngx_queue_t *locations);

static void ngx_http_create_locations_list(ngx_queue_t *locations,

    ngx_queue_t *q);

static ngx_http_location_tree_node_t *

    ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,

    size_t prefix);

static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf,

    ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports);

static ngx_int_t ngx_http_server_names(ngx_conf_t *cf,

    ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr);

static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two);

static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,

    const void *two);

static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,

    ngx_http_conf_port_t *port);

static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf,

    ngx_http_conf_addr_t *addr);

static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,

    ngx_http_conf_addr_t *addr);

#if (NGX_HAVE_INET6)

static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,

    ngx_http_conf_addr_t *addr);

#endif

‌ngx_uint_t   ngx_http_max_module;

‌ngx_int_t  (*ngx_http_top_header_filter) (ngx_http_request_t *r);

‌ngx_int_t  (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);

‌ngx_str_t  ngx_http_html_default_types[] = {

    ngx_string("text/html"),

    ngx_null_string

};

‌static ngx_command_t  ngx_http_commands[] = {

    { ngx_string("http"),

      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_http_block,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_core_module_t  ngx_http_module_ctx = {

    ngx_string("http"),

    NULL,

    NULL

};

‌ngx_module_t  ngx_http_module = {

    NGX_MODULE_V1,

    &ngx_http_module_ctx,                  /* module context */

    ngx_http_commands,                     /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static char *

‌ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                        *rv;

    ngx_uint_t                   mi, m, s;

    ngx_conf_t                   pcf;

    ngx_http_module_t           *module;

    ngx_http_conf_ctx_t         *ctx;

    ngx_http_core_loc_conf_t    *clcf;

    ngx_http_core_srv_conf_t   **cscfp;

    ngx_http_core_main_conf_t   *cmcf;

    /* the main http context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *(ngx_http_conf_ctx_t **) conf = ctx;

    /* count the number of the http modules and set up their indices */

    ngx_http_max_module = 0;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        ngx_modules[m]->ctx_index = ngx_http_max_module++;

    }

    /* the http main_conf context, it is the same in the all http contexts */

    ctx->main_conf = ngx_pcalloc(cf->pool,

                                 sizeof(void *) * ngx_http_max_module);

    if (ctx->main_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /*

     * the http null srv_conf context, it is used to merge

     * the server{}s' srv_conf's

     */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->srv_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /*

     * the http null loc_conf context, it is used to merge

     * the server{}s' loc_conf's

     */

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /*

     * create the main_conf's, the null srv_conf's, and the null loc_conf's

     * of the all http modules

     */

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        mi = ngx_modules[m]->ctx_index;

        if (module->create_main_conf) {

            ctx->main_conf[mi] = module->create_main_conf(cf);

            if (ctx->main_conf[mi] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        if (module->create_srv_conf) {

            ctx->srv_conf[mi] = module->create_srv_conf(cf);

            if (ctx->srv_conf[mi] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        if (module->create_loc_conf) {

            ctx->loc_conf[mi] = module->create_loc_conf(cf);

            if (ctx->loc_conf[mi] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

    }

    pcf = *cf;

    cf->ctx = ctx;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->preconfiguration) {

            if (module->preconfiguration(cf) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    }

    /* parse inside the http{} block */

    cf->module_type = NGX_HTTP_MODULE;

    cf->cmd_type = NGX_HTTP_MAIN_CONF;

    rv = ngx_conf_parse(cf, NULL);

    if (rv != NGX_CONF_OK) {

        goto failed;

    }

    /*

     * init http{} main_conf's, merge the server{}s' srv_conf's

     * and its location{}s' loc_conf's

     */

    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

    cscfp = cmcf->servers.elts;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        mi = ngx_modules[m]->ctx_index;

        /* init http{} main_conf's */

        if (module->init_main_conf) {

            rv = module->init_main_conf(cf, ctx->main_conf[mi]);

            if (rv != NGX_CONF_OK) {

                goto failed;

            }

        }

        rv = ngx_http_merge_servers(cf, cmcf, module, mi);

        if (rv != NGX_CONF_OK) {

            goto failed;

        }

    }

    /* create location trees */

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

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

        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->postconfiguration) {

            if (module->postconfiguration(cf) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    }

    if (ngx_http_variables_init_vars(cf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    /*

     * http{}'s cf->ctx was needed while the configuration merging

     * and in postconfiguration process

     */

    *cf = pcf;

    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    /* optimize the lists of ports, addresses and server names */

    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

failed:

    *cf = pcf;

    return rv;

}

static ngx_int_t

‌ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)

{

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,

                       cf->pool, 1, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,

                       cf->pool, 1, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,

                       cf->pool, 1, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,

                       cf->pool, 1, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,

                       cf->pool, 2, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,

                       cf->pool, 4, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,

                       cf->pool, 1, sizeof(ngx_http_handler_pt))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)

{

    ngx_array_t         headers_in;

    ngx_hash_key_t     *hk;

    ngx_hash_init_t     hash;

    ngx_http_header_t  *header;

    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    for (header = ngx_http_headers_in; header->name.len; header++) {

        hk = ngx_array_push(&headers_in);

        if (hk == NULL) {

            return NGX_ERROR;

        }

        hk->key = header->name;

        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);

        hk->value = header;

    }

    hash.hash = &cmcf->headers_in_hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = 512;

    hash.bucket_size = ngx_align(64, ngx_cacheline_size);

    hash.name = "headers_in_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)

{

    ngx_int_t                   j;

    ngx_uint_t                  i, n;

    ngx_uint_t                  find_config_index, use_rewrite, use_access;

    ngx_http_handler_pt        *h;

    ngx_http_phase_handler_t   *ph;

    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;

    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;

    find_config_index = 0;

    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;

    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {

        n += cmcf->phases[i].handlers.nelts;

    }

    ph = ngx_pcalloc(cf->pool,

                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));

    if (ph == NULL) {

        return NGX_ERROR;

    }

    cmcf->phase_engine.handlers = ph;

    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {

        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:

            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {

                cmcf->phase_engine.server_rewrite_index = n;

            }

            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:

            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;

            n++;

            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:

            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {

                cmcf->phase_engine.location_rewrite_index = n;

            }

            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:

            if (use_rewrite) {

                ph->checker = ngx_http_core_post_rewrite_phase;

                ph->next = find_config_index;

                n++;

                ph++;

            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:

            checker = ngx_http_core_access_phase;

            n++;

            break;

        case NGX_HTTP_POST_ACCESS_PHASE:

            if (use_access) {

                ph->checker = ngx_http_core_post_access_phase;

                ph->next = n;

                ph++;

            }

            continue;

        case NGX_HTTP_TRY_FILES_PHASE:

            if (cmcf->try_files) {

                ph->checker = ngx_http_core_try_files_phase;

                n++;

                ph++;

            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:

            checker = ngx_http_core_content_phase;

            break;

        default:

            checker = ngx_http_core_generic_phase;

        }

        n += cmcf->phases[i].handlers.nelts;

        for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {

            ph->checker = checker;

            ph->handler = h[j];

            ph->next = n;

            ph++;

        }

    }

    return NGX_OK;

}

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;

    cscfp = cmcf->servers.elts;

    ctx = (ngx_http_conf_ctx_t *) cf->ctx;

    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) {

            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) {

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

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

            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;

            }

            /* merge the locations{}' loc_conf's */

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

            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;

}

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;

        rv = module->merge_loc_conf(cf, loc_conf[ctx_index],

                                    clcf->loc_conf[ctx_index]);

        if (rv != NGX_CONF_OK) {

            return rv;

        }

        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;

}

static ngx_int_t

‌ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_core_loc_conf_t *pclcf)

{

    ngx_uint_t                   n;

    ngx_queue_t                 *q, *locations, *named, tail;

    ngx_http_core_loc_conf_t    *clcf;

    ngx_http_location_queue_t   *lq;

    ngx_http_core_loc_conf_t   **clcfp;

#if (NGX_PCRE)

    ngx_uint_t                   r;

    ngx_queue_t                 *regex;

#endif

    locations = pclcf->locations;

    if (locations == NULL) {

        return NGX_OK;

    }

    ngx_queue_sort(locations, ngx_http_cmp_locations);

    named = NULL;

    n = 0;

#if (NGX_PCRE)

    regex = NULL;

    r = 0;

#endif

    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;

        if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {

            return NGX_ERROR;

        }

#if (NGX_PCRE)

        if (clcf->regex) {

            r++;

            if (regex == NULL) {

                regex = q;

            }

            continue;

        }

#endif

        if (clcf->named) {

            n++;

            if (named == NULL) {

                named = q;

            }

            continue;

        }

        if (clcf->noname) {

            break;

        }

    }

    if (q != ngx_queue_sentinel(locations)) {

        ngx_queue_split(locations, q, &tail);

    }

    if (named) {

        clcfp = ngx_palloc(cf->pool,

                           (n + 1) * sizeof(ngx_http_core_loc_conf_t *));

        if (clcfp == NULL) {

            return NGX_ERROR;

        }

        cscf->named_locations = clcfp;

        for (q = named;

             q != ngx_queue_sentinel(locations);

             q = ngx_queue_next(q))

        {

            lq = (ngx_http_location_queue_t *) q;

            *(clcfp++) = lq->exact;

        }

        *clcfp = NULL;

        ngx_queue_split(locations, named, &tail);

    }

#if (NGX_PCRE)

    if (regex) {

        clcfp = ngx_palloc(cf->pool,

                           (r + 1) * sizeof(ngx_http_core_loc_conf_t *));

        if (clcfp == NULL) {

            return NGX_ERROR;

        }

        pclcf->regex_locations = clcfp;

        for (q = regex;

             q != ngx_queue_sentinel(locations);

             q = ngx_queue_next(q))

        {

            lq = (ngx_http_location_queue_t *) q;

            *(clcfp++) = lq->exact;

        }

        *clcfp = NULL;

        ngx_queue_split(locations, regex, &tail);

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_init_static_location_trees(ngx_conf_t *cf,

    ngx_http_core_loc_conf_t *pclcf)

{

    ngx_queue_t                *q, *locations;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_location_queue_t  *lq;

    locations = pclcf->locations;

    if (locations == NULL) {

        return NGX_OK;

    }

    if (ngx_queue_empty(locations)) {

        return NGX_OK;

    }

    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;

        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_http_create_locations_list(locations, ngx_queue_head(locations));

    pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0);

    if (pclcf->static_locations == NULL) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,

    ngx_http_core_loc_conf_t *clcf)

{

    ngx_http_location_queue_t  *lq;

    if (*locations == NULL) {

        *locations = ngx_palloc(cf->temp_pool,

                                sizeof(ngx_http_location_queue_t));

        if (*locations == NULL) {

            return NGX_ERROR;

        }

        ngx_queue_init(*locations);

    }

    lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));

    if (lq == NULL) {

        return NGX_ERROR;

    }

    if (clcf->exact_match

#if (NGX_PCRE)

        || clcf->regex

#endif

        || clcf->named || clcf->noname)

    {

        lq->exact = clcf;

        lq->inclusive = NULL;

    } else {

        lq->exact = NULL;

        lq->inclusive = clcf;

    }

    lq->name = &clcf->name;

    lq->file_name = cf->conf_file->file.name.data;

    lq->line = cf->conf_file->line;

    ngx_queue_init(&lq->list);

    ngx_queue_insert_tail(*locations, &lq->queue);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two)

{

    ngx_int_t                   rc;

    ngx_http_core_loc_conf_t   *first, *second;

    ngx_http_location_queue_t  *lq1, *lq2;

    lq1 = (ngx_http_location_queue_t *) one;

    lq2 = (ngx_http_location_queue_t *) two;

    first = lq1->exact ? lq1->exact : lq1->inclusive;

    second = lq2->exact ? lq2->exact : lq2->inclusive;

    if (first->noname && !second->noname) {

        /* shift no named locations to the end */

        return 1;

    }

    if (!first->noname && second->noname) {

        /* shift no named locations to the end */

        return -1;

    }

    if (first->noname || second->noname) {

        /* do not sort no named locations */

        return 0;

    }

    if (first->named && !second->named) {

        /* shift named locations to the end */

        return 1;

    }

    if (!first->named && second->named) {

        /* shift named locations to the end */

        return -1;

    }

    if (first->named && second->named) {

        return ngx_strcmp(first->name.data, second->name.data);

    }

#if (NGX_PCRE)

    if (first->regex && !second->regex) {

        /* shift the regex matches to the end */

        return 1;

    }

    if (!first->regex && second->regex) {

        /* shift the regex matches to the end */

        return -1;

    }

    if (first->regex || second->regex) {

        /* do not sort the regex matches */

        return 0;

    }

#endif

    rc = ngx_filename_cmp(first->name.data, second->name.data,

                          ngx_min(first->name.len, second->name.len) + 1);

    if (rc == 0 && !first->exact_match && second->exact_match) {

        /* an exact match must be before the same inclusive one */

        return 1;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_join_exact_locations(ngx_conf_t *cf, ngx_queue_t *locations)

{

    ngx_queue_t                *q, *x;

    ngx_http_location_queue_t  *lq, *lx;

    q = ngx_queue_head(locations);

    while (q != ngx_queue_last(locations)) {

        x = ngx_queue_next(q);

        lq = (ngx_http_location_queue_t *) q;

        lx = (ngx_http_location_queue_t *) x;

        if (lq->name->len == lx->name->len

            && ngx_filename_cmp(lq->name->data, lx->name->data, lx->name->len)

               == 0)

        {

            if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) {

                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                              "duplicate location \"%V\" in %s:%ui",

                              lx->name, lx->file_name, lx->line);

                return NGX_ERROR;

            }

            lq->inclusive = lx->inclusive;

            ngx_queue_remove(x);

            continue;

        }

        q = ngx_queue_next(q);

    }

    return NGX_OK;

}

static void

‌ngx_http_create_locations_list(ngx_queue_t *locations, ngx_queue_t *q)

{

    u_char                     *name;

    size_t                      len;

    ngx_queue_t                *x, tail;

    ngx_http_location_queue_t  *lq, *lx;

    if (q == ngx_queue_last(locations)) {

        return;

    }

    lq = (ngx_http_location_queue_t *) q;

    if (lq->inclusive == NULL) {

        ngx_http_create_locations_list(locations, ngx_queue_next(q));

        return;

    }

    len = lq->name->len;

    name = lq->name->data;

    for (x = ngx_queue_next(q);

         x != ngx_queue_sentinel(locations);

         x = ngx_queue_next(x))

    {

        lx = (ngx_http_location_queue_t *) x;

        if (len > lx->name->len

            || ngx_filename_cmp(name, lx->name->data, len) != 0)

        {

            break;

        }

    }

    q = ngx_queue_next(q);

    if (q == x) {

        ngx_http_create_locations_list(locations, x);

        return;

    }

    ngx_queue_split(locations, q, &tail);

    ngx_queue_add(&lq->list, &tail);

    if (x == ngx_queue_sentinel(locations)) {

        ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));

        return;

    }

    ngx_queue_split(&lq->list, x, &tail);

    ngx_queue_add(locations, &tail);

    ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));

    ngx_http_create_locations_list(locations, x);

}

/*

* to keep cache locality for left leaf nodes, allocate nodes in following

* order: node, left subtree, right subtree, inclusive subtree

*/

static ngx_http_location_tree_node_t *

‌ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,

    size_t prefix)

{

    size_t                          len;

    ngx_queue_t                    *q, tail;

    ngx_http_location_queue_t      *lq;

    ngx_http_location_tree_node_t  *node;

    q = ngx_queue_middle(locations);

    lq = (ngx_http_location_queue_t *) q;

    len = lq->name->len - prefix;

    node = ngx_palloc(cf->pool,

                      offsetof(ngx_http_location_tree_node_t, name) + len);

    if (node == NULL) {

        return NULL;

    }

    node->left = NULL;

    node->right = NULL;

    node->tree = NULL;

    node->exact = lq->exact;

    node->inclusive = lq->inclusive;

    node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)

                           || (lq->inclusive && lq->inclusive->auto_redirect));

    node->len = (u_char) len;

    ngx_memcpy(node->name, &lq->name->data[prefix], len);

    ngx_queue_split(locations, q, &tail);

    if (ngx_queue_empty(locations)) {

        /*

         * ngx_queue_split() insures that if left part is empty,

         * then right one is empty too

         */

        goto inclusive;

    }

    node->left = ngx_http_create_locations_tree(cf, locations, prefix);

    if (node->left == NULL) {

        return NULL;

    }

    ngx_queue_remove(q);

    if (ngx_queue_empty(&tail)) {

        goto inclusive;

    }

    node->right = ngx_http_create_locations_tree(cf, &tail, prefix);

    if (node->right == NULL) {

        return NULL;

    }

inclusive:

    if (ngx_queue_empty(&lq->list)) {

        return node;

    }

    node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);

    if (node->tree == NULL) {

        return NULL;

    }

    return node;

}

ngx_int_t

‌ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_listen_opt_t *lsopt)

{

    in_port_t                   p;

    ngx_uint_t                  i;

    struct sockaddr            *sa;

    struct sockaddr_in         *sin;

    ngx_http_conf_port_t       *port;

    ngx_http_core_main_conf_t  *cmcf;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6        *sin6;

#endif

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    if (cmcf->ports == NULL) {

        cmcf->ports = ngx_array_create(cf->temp_pool, 2,

                                       sizeof(ngx_http_conf_port_t));

        if (cmcf->ports == NULL) {

            return NGX_ERROR;

        }

    }

    sa = &lsopt->u.sockaddr;

    switch (sa->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = &lsopt->u.sockaddr_in6;

        p = sin6->sin6_port;

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        p = 0;

        break;

#endif

    default: /* AF_INET */

        sin = &lsopt->u.sockaddr_in;

        p = sin->sin_port;

        break;

    }

    port = cmcf->ports->elts;

    for (i = 0; i < cmcf->ports->nelts; i++) {

        if (p != port[i].port || sa->sa_family != port[i].family) {

            continue;

        }

        /* a port is already in the port list */

        return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);

    }

    /* add a port to the port list */

    port = ngx_array_push(cmcf->ports);

    if (port == NULL) {

        return NGX_ERROR;

    }

    port->family = sa->sa_family;

    port->port = p;

    port->addrs.elts = NULL;

    return ngx_http_add_address(cf, cscf, port, lsopt);

}

static ngx_int_t

‌ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)

{

    u_char                *p;

    size_t                 len, off;

    ngx_uint_t             i, default_server;

    struct sockaddr       *sa;

    ngx_http_conf_addr_t  *addr;

#if (NGX_HAVE_UNIX_DOMAIN)

    struct sockaddr_un    *saun;

#endif

#if (NGX_HTTP_SSL)

    ngx_uint_t             ssl;

#endif

#if (NGX_HTTP_SPDY)

    ngx_uint_t             spdy;

#endif

    /*

     * we cannot compare whole sockaddr struct's as kernel

     * may fill some fields in inherited sockaddr struct's

     */

    sa = &lsopt->u.sockaddr;

    switch (sa->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        off = offsetof(struct sockaddr_in6, sin6_addr);

        len = 16;

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        off = offsetof(struct sockaddr_un, sun_path);

        len = sizeof(saun->sun_path);

        break;

#endif

    default: /* AF_INET */

        off = offsetof(struct sockaddr_in, sin_addr);

        len = 4;

        break;

    }

    p = lsopt->u.sockaddr_data + off;

    addr = port->addrs.elts;

    for (i = 0; i < port->addrs.nelts; i++) {

        if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) {

            continue;

        }

        /* the address is already in the address list */

        if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) {

            return NGX_ERROR;

        }

        /* preserve default_server bit during listen options overwriting */

        default_server = addr[i].opt.default_server;

#if (NGX_HTTP_SSL)

        ssl = lsopt->ssl || addr[i].opt.ssl;

#endif

#if (NGX_HTTP_SPDY)

        spdy = lsopt->spdy || addr[i].opt.spdy;

#endif

        if (lsopt->set) {

            if (addr[i].opt.set) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "duplicate listen options for %s", addr[i].opt.addr);

                return NGX_ERROR;

            }

            addr[i].opt = *lsopt;

        }

        /* check the duplicate "default" server for this address:port */

        if (lsopt->default_server) {

            if (default_server) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "a duplicate default server for %s", addr[i].opt.addr);

                return NGX_ERROR;

            }

            default_server = 1;

            addr[i].default_server = cscf;

        }

        addr[i].opt.default_server = default_server;

#if (NGX_HTTP_SSL)

        addr[i].opt.ssl = ssl;

#endif

#if (NGX_HTTP_SPDY)

        addr[i].opt.spdy = spdy;

#endif

        return NGX_OK;

    }

    /* add the address to the addresses list that bound to this port */

    return ngx_http_add_address(cf, cscf, port, lsopt);

}

/*

* add the server address, the server names and the server core module

* configurations to the port list

*/

static ngx_int_t

‌ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)

{

    ngx_http_conf_addr_t  *addr;

    if (port->addrs.elts == NULL) {

        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,

                           sizeof(ngx_http_conf_addr_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

#if (NGX_HTTP_SPDY && NGX_HTTP_SSL                                            \

     && !defined TLSEXT_TYPE_application_layer_protocol_negotiation           \

     && !defined TLSEXT_TYPE_next_proto_neg)

    if (lsopt->spdy && lsopt->ssl) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "nginx was built without OpenSSL ALPN or NPN "

                           "support, SPDY is not enabled for %s", lsopt->addr);

    }

#endif

    addr = ngx_array_push(&port->addrs);

    if (addr == NULL) {

        return NGX_ERROR;

    }

    addr->opt = *lsopt;

    addr->hash.buckets = NULL;

    addr->hash.size = 0;

    addr->wc_head = NULL;

    addr->wc_tail = NULL;

#if (NGX_PCRE)

    addr->nregex = 0;

    addr->regex = NULL;

#endif

    addr->default_server = cscf;

    addr->servers.elts = NULL;

    return ngx_http_add_server(cf, cscf, addr);

}

/* add the server core module configuration to the address:port */

static ngx_int_t

‌ngx_http_add_server(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,

    ngx_http_conf_addr_t *addr)

{

    ngx_uint_t                  i;

    ngx_http_core_srv_conf_t  **server;

    if (addr->servers.elts == NULL) {

        if (ngx_array_init(&addr->servers, cf->temp_pool, 4,

                           sizeof(ngx_http_core_srv_conf_t *))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    } else {

        server = addr->servers.elts;

        for (i = 0; i < addr->servers.nelts; i++) {

            if (server[i] == cscf) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "a duplicate listen %s", addr->opt.addr);

                return NGX_ERROR;

            }

        }

    }

    server = ngx_array_push(&addr->servers);

    if (server == NULL) {

        return NGX_ERROR;

    }

    *server = cscf;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,

    ngx_array_t *ports)

{

    ngx_uint_t             p, a;

    ngx_http_conf_port_t  *port;

    ngx_http_conf_addr_t  *addr;

    if (ports == NULL) {

        return NGX_OK;

    }

    port = ports->elts;

    for (p = 0; p < ports->nelts; p++) {

        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,

                 sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);

        /*

         * check whether all name-based servers have the same

         * configuration as a default server for given address:port

         */

        addr = port[p].addrs.elts;

        for (a = 0; a < port[p].addrs.nelts; a++) {

            if (addr[a].servers.nelts > 1

#if (NGX_PCRE)

                || addr[a].default_server->captures

#endif

               )

            {

                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {

                    return NGX_ERROR;

                }

            }

        }

        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,

    ngx_http_conf_addr_t *addr)

{

    ngx_int_t                   rc;

    ngx_uint_t                  n, s;

    ngx_hash_init_t             hash;

    ngx_hash_keys_arrays_t      ha;

    ngx_http_server_name_t     *name;

    ngx_http_core_srv_conf_t  **cscfp;

#if (NGX_PCRE)

    ngx_uint_t                  regex, i;

    regex = 0;

#endif

    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));

    ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);

    if (ha.temp_pool == NULL) {

        return NGX_ERROR;

    }

    ha.pool = cf->pool;

    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {

        goto failed;

    }

    cscfp = addr->servers.elts;

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

        name = cscfp[s]->server_names.elts;

        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {

#if (NGX_PCRE)

            if (name[n].regex) {

                regex++;

                continue;

            }

#endif

            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,

                                  NGX_HASH_WILDCARD_KEY);

            if (rc == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (rc == NGX_DECLINED) {

                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                              "invalid server name or wildcard \"%V\" on %s",

                              &name[n].name, addr->opt.addr);

                return NGX_ERROR;

            }

            if (rc == NGX_BUSY) {

                ngx_log_error(NGX_LOG_WARN, cf->log, 0,

                              "conflicting server name \"%V\" on %s, ignored",

                              &name[n].name, addr->opt.addr);

            }

        }

    }

    hash.key = ngx_hash_key_lc;

    hash.max_size = cmcf->server_names_hash_max_size;

    hash.bucket_size = cmcf->server_names_hash_bucket_size;

    hash.name = "server_names_hash";

    hash.pool = cf->pool;

    if (ha.keys.nelts) {

        hash.hash = &addr->hash;

        hash.temp_pool = NULL;

        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {

            goto failed;

        }

    }

    if (ha.dns_wc_head.nelts) {

        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,

                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);

        hash.hash = NULL;

        hash.temp_pool = ha.temp_pool;

        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,

                                   ha.dns_wc_head.nelts)

            != NGX_OK)

        {

            goto failed;

        }

        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;

    }

    if (ha.dns_wc_tail.nelts) {

        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,

                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);

        hash.hash = NULL;

        hash.temp_pool = ha.temp_pool;

        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,

                                   ha.dns_wc_tail.nelts)

            != NGX_OK)

        {

            goto failed;

        }

        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;

    }

    ngx_destroy_pool(ha.temp_pool);

#if (NGX_PCRE)

    if (regex == 0) {

        return NGX_OK;

    }

    addr->nregex = regex;

    addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t));

    if (addr->regex == NULL) {

        return NGX_ERROR;

    }

    i = 0;

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

        name = cscfp[s]->server_names.elts;

        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {

            if (name[n].regex) {

                addr->regex[i++] = name[n];

            }

        }

    }

#endif

    return NGX_OK;

failed:

    ngx_destroy_pool(ha.temp_pool);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_cmp_conf_addrs(const void *one, const void *two)

{

    ngx_http_conf_addr_t  *first, *second;

    first = (ngx_http_conf_addr_t *) one;

    second = (ngx_http_conf_addr_t *) two;

    if (first->opt.wildcard) {

        /* a wildcard address must be the last resort, shift it to the end */

        return 1;

    }

    if (second->opt.wildcard) {

        /* a wildcard address must be the last resort, shift it to the end */

        return -1;

    }

    if (first->opt.bind && !second->opt.bind) {

        /* shift explicit bind()ed addresses to the start */

        return -1;

    }

    if (!first->opt.bind && second->opt.bind) {

        /* shift explicit bind()ed addresses to the start */

        return 1;

    }

    /* do not sort by default */

    return 0;

}

static int ngx_libc_cdecl

‌ngx_http_cmp_dns_wildcards(const void *one, const void *two)

{

    ngx_hash_key_t  *first, *second;

    first = (ngx_hash_key_t *) one;

    second = (ngx_hash_key_t *) two;

    return ngx_dns_strcmp(first->key.data, second->key.data);

}

static ngx_int_t

‌ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)

{

    ngx_uint_t                 i, last, bind_wildcard;

    ngx_listening_t           *ls;

    ngx_http_port_t           *hport;

    ngx_http_conf_addr_t      *addr;

    addr = port->addrs.elts;

    last = port->addrs.nelts;

    /*

     * If there is a binding to an "*:port" then we need to bind() to

     * the "*:port" only and ignore other implicit bindings.  The bindings

     * have been already sorted: explicit bindings are on the start, then

     * implicit bindings go, and wildcard binding is in the end.

     */

    if (addr[last - 1].opt.wildcard) {

        addr[last - 1].opt.bind = 1;

        bind_wildcard = 1;

    } else {

        bind_wildcard = 0;

    }

    i = 0;

    while (i < last) {

        if (bind_wildcard && !addr[i].opt.bind) {

            i++;

            continue;

        }

        ls = ngx_http_add_listening(cf, &addr[i]);

        if (ls == NULL) {

            return NGX_ERROR;

        }

        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));

        if (hport == NULL) {

            return NGX_ERROR;

        }

        ls->servers = hport;

        if (i == last - 1) {

            hport->naddrs = last;

        } else {

            hport->naddrs = 1;

            i = 0;

        }

        switch (ls->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {

                return NGX_ERROR;

            }

            break;

#endif

        default: /* AF_INET */

            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {

                return NGX_ERROR;

            }

            break;

        }

        addr++;

        last--;

    }

    return NGX_OK;

}

static ngx_listening_t *

‌ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)

{

    ngx_listening_t           *ls;

    ngx_http_core_loc_conf_t  *clcf;

    ngx_http_core_srv_conf_t  *cscf;

    ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);

    if (ls == NULL) {

        return NULL;

    }

    ls->addr_ntop = 1;

    ls->handler = ngx_http_init_connection;

    cscf = addr->default_server;

    ls->pool_size = cscf->connection_pool_size;

    ls->post_accept_timeout = cscf->client_header_timeout;

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

    ls->logp = clcf->error_log;

    ls->log.data = &ls->addr_text;

    ls->log.handler = ngx_accept_log_error;

#if (NGX_WIN32)

    {

    ngx_iocp_conf_t  *iocpcf = NULL;

    if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {

        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);

    }

    if (iocpcf && iocpcf->acceptex_read) {

        ls->post_accept_buffer_size = cscf->client_header_buffer_size;

    }

    }

#endif

    ls->backlog = addr->opt.backlog;

    ls->rcvbuf = addr->opt.rcvbuf;

    ls->sndbuf = addr->opt.sndbuf;

    ls->keepalive = addr->opt.so_keepalive;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    ls->keepidle = addr->opt.tcp_keepidle;

    ls->keepintvl = addr->opt.tcp_keepintvl;

    ls->keepcnt = addr->opt.tcp_keepcnt;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

    ls->accept_filter = addr->opt.accept_filter;

#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

    ls->deferred_accept = addr->opt.deferred_accept;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    ls->ipv6only = addr->opt.ipv6only;

#endif

#if (NGX_HAVE_SETFIB)

    ls->setfib = addr->opt.setfib;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

    ls->fastopen = addr->opt.fastopen;

#endif

    return ls;

}

static ngx_int_t

‌ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,

    ngx_http_conf_addr_t *addr)

{

    ngx_uint_t                 i;

    ngx_http_in_addr_t        *addrs;

    struct sockaddr_in        *sin;

    ngx_http_virtual_names_t  *vn;

    hport->addrs = ngx_pcalloc(cf->pool,

                               hport->naddrs * sizeof(ngx_http_in_addr_t));

    if (hport->addrs == NULL) {

        return NGX_ERROR;

    }

    addrs = hport->addrs;

    for (i = 0; i < hport->naddrs; i++) {

        sin = &addr[i].opt.u.sockaddr_in;

        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.default_server = addr[i].default_server;

#if (NGX_HTTP_SSL)

        addrs[i].conf.ssl = addr[i].opt.ssl;

#endif

#if (NGX_HTTP_SPDY)

        addrs[i].conf.spdy = addr[i].opt.spdy;

#endif

        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;

        if (addr[i].hash.buckets == NULL

            && (addr[i].wc_head == NULL

                || addr[i].wc_head->hash.buckets == NULL)

            && (addr[i].wc_tail == NULL

                || addr[i].wc_tail->hash.buckets == NULL)

#if (NGX_PCRE)

            && addr[i].nregex == 0

#endif

            )

        {

            continue;

        }

        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));

        if (vn == NULL) {

            return NGX_ERROR;

        }

        addrs[i].conf.virtual_names = vn;

        vn->names.hash = addr[i].hash;

        vn->names.wc_head = addr[i].wc_head;

        vn->names.wc_tail = addr[i].wc_tail;

#if (NGX_PCRE)

        vn->nregex = addr[i].nregex;

        vn->regex = addr[i].regex;

#endif

    }

    return NGX_OK;

}

#if (NGX_HAVE_INET6)

static ngx_int_t

‌ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,

    ngx_http_conf_addr_t *addr)

{

    ngx_uint_t                 i;

    ngx_http_in6_addr_t       *addrs6;

    struct sockaddr_in6       *sin6;

    ngx_http_virtual_names_t  *vn;

    hport->addrs = ngx_pcalloc(cf->pool,

                               hport->naddrs * sizeof(ngx_http_in6_addr_t));

    if (hport->addrs == NULL) {

        return NGX_ERROR;

    }

    addrs6 = hport->addrs;

    for (i = 0; i < hport->naddrs; i++) {

        sin6 = &addr[i].opt.u.sockaddr_in6;

        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.default_server = addr[i].default_server;

#if (NGX_HTTP_SSL)

        addrs6[i].conf.ssl = addr[i].opt.ssl;

#endif

#if (NGX_HTTP_SPDY)

        addrs6[i].conf.spdy = addr[i].opt.spdy;

#endif

        if (addr[i].hash.buckets == NULL

            && (addr[i].wc_head == NULL

                || addr[i].wc_head->hash.buckets == NULL)

            && (addr[i].wc_tail == NULL

                || addr[i].wc_tail->hash.buckets == NULL)

#if (NGX_PCRE)

            && addr[i].nregex == 0

#endif

            )

        {

            continue;

        }

        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));

        if (vn == NULL) {

            return NGX_ERROR;

        }

        addrs6[i].conf.virtual_names = vn;

        vn->names.hash = addr[i].hash;

        vn->names.wc_head = addr[i].wc_head;

        vn->names.wc_tail = addr[i].wc_tail;

#if (NGX_PCRE)

        vn->nregex = addr[i].nregex;

        vn->regex = addr[i].regex;

#endif

    }

    return NGX_OK;

}

#endif

char *

‌ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_array_t     **types;

    ngx_str_t        *value, *default_type;

    ngx_uint_t        i, n, hash;

    ngx_hash_key_t   *type;

    types = (ngx_array_t **) (p + cmd->offset);

    if (*types == (void *) -1) {

        return NGX_CONF_OK;

    }

    default_type = cmd->post;

    if (*types == NULL) {

        *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));

        if (*types == NULL) {

            return NGX_CONF_ERROR;

        }

        if (default_type) {

            type = ngx_array_push(*types);

            if (type == NULL) {

                return NGX_CONF_ERROR;

            }

            type->key = *default_type;

            type->key_hash = ngx_hash_key(default_type->data,

                                          default_type->len);

            type->value = (void *) 4;

        }

    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (value[i].len == 1 && value[i].data[0] == '*') {

            *types = (void *) -1;

            return NGX_CONF_OK;

        }

        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);

        value[i].data[value[i].len] = '\0';

        type = (*types)->elts;

        for (n = 0; n < (*types)->nelts; n++) {

            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {

                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                                   "duplicate MIME type \"%V\"", &value[i]);

                goto next;

            }

        }

        type = ngx_array_push(*types);

        if (type == NULL) {

            return NGX_CONF_ERROR;

        }

        type->key = value[i];

        type->key_hash = hash;

        type->value = (void *) 4;

    next:

        continue;

    }

    return NGX_CONF_OK;

}

char *

‌ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys, ngx_hash_t *types_hash,

    ngx_array_t **prev_keys, ngx_hash_t *prev_types_hash,

    ngx_str_t *default_types)

{

    ngx_hash_init_t  hash;

    if (*keys) {

        if (*keys == (void *) -1) {

            return NGX_CONF_OK;

        }

        hash.hash = types_hash;

        hash.key = NULL;

        hash.max_size = 2048;

        hash.bucket_size = 64;

        hash.name = "test_types_hash";

        hash.pool = cf->pool;

        hash.temp_pool = NULL;

        if (ngx_hash_init(&hash, (*keys)->elts, (*keys)->nelts) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    if (prev_types_hash->buckets == NULL) {

        if (*prev_keys == NULL) {

            if (ngx_http_set_default_types(cf, prev_keys, default_types)

                != NGX_OK)

            {

                return NGX_CONF_ERROR;

            }

        } else if (*prev_keys == (void *) -1) {

            *keys = *prev_keys;

            return NGX_CONF_OK;

        }

        hash.hash = prev_types_hash;

        hash.key = NULL;

        hash.max_size = 2048;

        hash.bucket_size = 64;

        hash.name = "test_types_hash";

        hash.pool = cf->pool;

        hash.temp_pool = NULL;

        if (ngx_hash_init(&hash, (*prev_keys)->elts, (*prev_keys)->nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    *types_hash = *prev_types_hash;

    return NGX_CONF_OK;

}

ngx_int_t

‌ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,

    ngx_str_t *default_type)

{

    ngx_hash_key_t  *type;

    *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));

    if (*types == NULL) {

        return NGX_ERROR;

    }

    while (default_type->len) {

        type = ngx_array_push(*types);

        if (type == NULL) {

            return NGX_ERROR;

        }

        type->key = *default_type;

        type->key_hash = ngx_hash_key(default_type->data,

                                      default_type->len);

        type->value = (void *) 4;

        default_type++;

    }

    return NGX_OK;

}

src/http/ngx_http_core_module.c - nginx-1.7.9

Global variables defined

ngx_conf_deprecated_open_file_cache_retest

ngx_conf_deprecated_optimize_server_names

ngx_conf_deprecated_satisfy_any

ngx_http_client_temp_path

ngx_http_core_aio

ngx_http_core_commands

ngx_http_core_default_types

ngx_http_core_get_method

ngx_http_core_if_modified_since

ngx_http_core_image_gif_type

ngx_http_core_image_jpeg_type

ngx_http_core_keepalive_disable

ngx_http_core_lingering_close

ngx_http_core_lowat_post

ngx_http_core_module

ngx_http_core_module_ctx

ngx_http_core_pool_size_p

ngx_http_core_request_body_in_file

ngx_http_core_satisfy

ngx_http_core_text_html_type

ngx_http_gzip_http_version

ngx_http_gzip_no_cache

ngx_http_gzip_no_store

ngx_http_gzip_private

ngx_http_gzip_proxied_mask

ngx_methods_names

Data types defined

ngx_http_method_name_t

Functions defined

ngx_http_auth_basic_user

ngx_http_cleanup_add

ngx_http_core_access_phase

ngx_http_core_content_phase

ngx_http_core_create_loc_conf

ngx_http_core_create_main_conf

ngx_http_core_create_srv_conf

ngx_http_core_directio

ngx_http_core_error_log

ngx_http_core_error_page

ngx_http_core_find_config_phase

ngx_http_core_find_location

ngx_http_core_find_static_location

ngx_http_core_generic_phase

ngx_http_core_init_main_conf

ngx_http_core_internal

ngx_http_core_keepalive

ngx_http_core_limit_except

ngx_http_core_listen

ngx_http_core_location

ngx_http_core_lowat_check

ngx_http_core_merge_loc_conf

ngx_http_core_merge_srv_conf

ngx_http_core_open_file_cache

ngx_http_core_pool_size

ngx_http_core_post_access_phase

ngx_http_core_post_rewrite_phase

ngx_http_core_preconfiguration

ngx_http_core_regex_location

ngx_http_core_resolver

ngx_http_core_rewrite_phase

ngx_http_core_root

ngx_http_core_run_phases

ngx_http_core_server

ngx_http_core_server_name

ngx_http_core_try_files

ngx_http_core_try_files_phase

ngx_http_core_type

ngx_http_core_types

ngx_http_disable_symlinks

ngx_http_get_forwarded_addr

ngx_http_get_forwarded_addr_internal

ngx_http_gzip_accept_encoding

ngx_http_gzip_disable

ngx_http_gzip_ok

ngx_http_gzip_quantity

ngx_http_handler

ngx_http_internal_redirect

ngx_http_map_uri_to_path

ngx_http_named_location

ngx_http_output_filter

ngx_http_send_header

ngx_http_send_response

ngx_http_set_content_type

ngx_http_set_disable_symlinks

ngx_http_set_etag

ngx_http_set_exten

ngx_http_subrequest

ngx_http_test_content_type

ngx_http_update_location_config

ngx_http_weak_etag

Macros defined

NGX_HTTP_REQUEST_BODY_FILE_CLEAN

NGX_HTTP_REQUEST_BODY_FILE_OFF

NGX_HTTP_REQUEST_BODY_FILE_ON

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    u_char    *name;

    uint32_t   method;

‌} ngx_http_method_name_t;

‌#define NGX_HTTP_REQUEST_BODY_FILE_OFF    0

‌#define NGX_HTTP_REQUEST_BODY_FILE_ON     1

‌#define NGX_HTTP_REQUEST_BODY_FILE_CLEAN  2

static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);

static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r,

    ngx_http_location_tree_node_t *node);

static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf);

static void *ngx_http_core_create_main_conf(ngx_conf_t *cf);

static char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf);

static void *ngx_http_core_create_srv_conf(ngx_conf_t *cf);

static char *ngx_http_core_merge_srv_conf(ngx_conf_t *cf,

    void *parent, void *child);

static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd,

    void *dummy);

static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,

    void *dummy);

static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf,

    ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless);

static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy,

    void *conf);

static char *ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#if (NGX_HTTP_GZIP)

static ngx_int_t ngx_http_gzip_accept_encoding(ngx_str_t *ae);

static ngx_uint_t ngx_http_gzip_quantity(u_char *p, u_char *last);

static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

static ngx_int_t ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r,

    ngx_addr_t *addr, u_char *xff, size_t xfflen, ngx_array_t *proxies,

    int recursive);

#if (NGX_HAVE_OPENAT)

static char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);

‌static ngx_conf_post_t  ngx_http_core_lowat_post =

    { ngx_http_core_lowat_check };

‌static ngx_conf_post_handler_pt  ngx_http_core_pool_size_p =

    ngx_http_core_pool_size;

‌static ngx_conf_deprecated_t  ngx_conf_deprecated_optimize_server_names = {

    ngx_conf_deprecated, "optimize_server_names", "server_name_in_redirect"

};

‌static ngx_conf_deprecated_t  ngx_conf_deprecated_open_file_cache_retest = {

    ngx_conf_deprecated, "open_file_cache_retest", "open_file_cache_valid"

};

‌static ngx_conf_deprecated_t  ngx_conf_deprecated_satisfy_any = {

    ngx_conf_deprecated, "satisfy_any", "satisfy"

};

‌static ngx_conf_enum_t  ngx_http_core_request_body_in_file[] = {

    { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_OFF },

    { ngx_string("on"), NGX_HTTP_REQUEST_BODY_FILE_ON },

    { ngx_string("clean"), NGX_HTTP_REQUEST_BODY_FILE_CLEAN },

    { ngx_null_string, 0 }

};

#if (NGX_HAVE_FILE_AIO)

‌static ngx_conf_enum_t  ngx_http_core_aio[] = {

    { ngx_string("off"), NGX_HTTP_AIO_OFF  },

    { ngx_string("on"), NGX_HTTP_AIO_ON },

#if (NGX_HAVE_AIO_SENDFILE)

    { ngx_string("sendfile"), NGX_HTTP_AIO_SENDFILE },

#endif

    { ngx_null_string, 0 }

};

#endif

‌static ngx_conf_enum_t  ngx_http_core_satisfy[] = {

    { ngx_string("all"), NGX_HTTP_SATISFY_ALL },

    { ngx_string("any"), NGX_HTTP_SATISFY_ANY },

    { ngx_null_string, 0 }

};

‌static ngx_conf_enum_t  ngx_http_core_lingering_close[] = {

    { ngx_string("off"), NGX_HTTP_LINGERING_OFF },

    { ngx_string("on"), NGX_HTTP_LINGERING_ON },

    { ngx_string("always"), NGX_HTTP_LINGERING_ALWAYS },

    { ngx_null_string, 0 }

};

‌static ngx_conf_enum_t  ngx_http_core_if_modified_since[] = {

    { ngx_string("off"), NGX_HTTP_IMS_OFF },

    { ngx_string("exact"), NGX_HTTP_IMS_EXACT },

    { ngx_string("before"), NGX_HTTP_IMS_BEFORE },

    { ngx_null_string, 0 }

};

‌static ngx_conf_bitmask_t  ngx_http_core_keepalive_disable[] = {

    { ngx_string("none"), NGX_HTTP_KEEPALIVE_DISABLE_NONE },

    { ngx_string("msie6"), NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 },

    { ngx_string("safari"), NGX_HTTP_KEEPALIVE_DISABLE_SAFARI },

    { ngx_null_string, 0 }

};

‌static ngx_path_init_t  ngx_http_client_temp_path = {

    ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 }

};

#if (NGX_HTTP_GZIP)

‌static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {

    { ngx_string("1.0"), NGX_HTTP_VERSION_10 },

    { ngx_string("1.1"), NGX_HTTP_VERSION_11 },

    { ngx_null_string, 0 }

};

‌static ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {

    { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },

    { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },

    { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },

    { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },

    { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },

    { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },

    { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },

    { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },

    { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },

    { ngx_null_string, 0 }

};

‌static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");

‌static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");

‌static ngx_str_t  ngx_http_gzip_private = ngx_string("private");

#endif

‌static ngx_command_t  ngx_http_core_commands[] = {

    { ngx_string("variables_hash_max_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_core_main_conf_t, variables_hash_max_size),

      NULL },

    { ngx_string("variables_hash_bucket_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_core_main_conf_t, variables_hash_bucket_size),

      NULL },

    { ngx_string("server_names_hash_max_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_core_main_conf_t, server_names_hash_max_size),

      NULL },

    { ngx_string("server_names_hash_bucket_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_core_main_conf_t, server_names_hash_bucket_size),

      NULL },

    { ngx_string("server"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_http_core_server,

      0,

      0,

      NULL },

    { ngx_string("connection_pool_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, connection_pool_size),

      &ngx_http_core_pool_size_p },

    { ngx_string("request_pool_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, request_pool_size),

      &ngx_http_core_pool_size_p },

    { ngx_string("client_header_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, client_header_timeout),

      NULL },

    { ngx_string("client_header_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size),

      NULL },

    { ngx_string("large_client_header_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers),

      NULL },

    { ngx_string("optimize_server_names"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),

      &ngx_conf_deprecated_optimize_server_names },

    { ngx_string("ignore_invalid_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers),

      NULL },

    { ngx_string("merge_slashes"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, merge_slashes),

      NULL },

    { ngx_string("underscores_in_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, underscores_in_headers),

      NULL },

    { ngx_string("location"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,

      ngx_http_core_location,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("listen"),

      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,

      ngx_http_core_listen,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("server_name"),

      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,

      ngx_http_core_server_name,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("types_hash_max_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, types_hash_max_size),

      NULL },

    { ngx_string("types_hash_bucket_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, types_hash_bucket_size),

      NULL },

    { ngx_string("types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF

                                          |NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_http_core_types,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("default_type"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, default_type),

      NULL },

    { ngx_string("root"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE1,

      ngx_http_core_root,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("alias"),

      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_core_root,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("limit_except"),

      NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,

      ngx_http_core_limit_except,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("client_max_body_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_off_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_max_body_size),

      NULL },

    { ngx_string("client_body_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_body_buffer_size),

      NULL },

    { ngx_string("client_body_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_body_timeout),

      NULL },

    { ngx_string("client_body_temp_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_conf_set_path_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_body_temp_path),

      NULL },

    { ngx_string("client_body_in_file_only"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_body_in_file_only),

      &ngx_http_core_request_body_in_file },

    { ngx_string("client_body_in_single_buffer"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, client_body_in_single_buffer),

      NULL },

    { ngx_string("sendfile"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, sendfile),

      NULL },

    { ngx_string("sendfile_max_chunk"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),

      NULL },

#if (NGX_HAVE_FILE_AIO)

    { ngx_string("aio"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, aio),

      &ngx_http_core_aio },

#endif

    { ngx_string("read_ahead"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, read_ahead),

      NULL },

    { ngx_string("directio"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_core_directio,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("directio_alignment"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_off_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, directio_alignment),

      NULL },

    { ngx_string("tcp_nopush"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, tcp_nopush),

      NULL },

    { ngx_string("tcp_nodelay"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, tcp_nodelay),

      NULL },

    { ngx_string("send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, send_timeout),

      NULL },

    { ngx_string("send_lowat"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, send_lowat),

      &ngx_http_core_lowat_post },

    { ngx_string("postpone_output"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, postpone_output),

      NULL },

    { ngx_string("limit_rate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, limit_rate),

      NULL },

    { ngx_string("limit_rate_after"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, limit_rate_after),

      NULL },

    { ngx_string("keepalive_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_core_keepalive,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("keepalive_requests"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, keepalive_requests),

      NULL },

    { ngx_string("keepalive_disable"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, keepalive_disable),

      &ngx_http_core_keepalive_disable },

    { ngx_string("satisfy"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, satisfy),

      &ngx_http_core_satisfy },

    { ngx_string("satisfy_any"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, satisfy),

      &ngx_conf_deprecated_satisfy_any },

    { ngx_string("internal"),

      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

      ngx_http_core_internal,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("lingering_close"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, lingering_close),

      &ngx_http_core_lingering_close },

    { ngx_string("lingering_time"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, lingering_time),

      NULL },

    { ngx_string("lingering_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, lingering_timeout),

      NULL },

    { ngx_string("reset_timedout_connection"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection),

      NULL },

    { ngx_string("server_name_in_redirect"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),

      NULL },

    { ngx_string("port_in_redirect"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, port_in_redirect),

      NULL },

    { ngx_string("msie_padding"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, msie_padding),

      NULL },

    { ngx_string("msie_refresh"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, msie_refresh),

      NULL },

    { ngx_string("log_not_found"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, log_not_found),

      NULL },

    { ngx_string("log_subrequest"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, log_subrequest),

      NULL },

    { ngx_string("recursive_error_pages"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, recursive_error_pages),

      NULL },

    { ngx_string("server_tokens"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, server_tokens),

      NULL },

    { ngx_string("if_modified_since"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, if_modified_since),

      &ngx_http_core_if_modified_since },

    { ngx_string("max_ranges"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, max_ranges),

      NULL },

    { ngx_string("chunked_transfer_encoding"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),

      NULL },

    { ngx_string("etag"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, etag),

      NULL },

    { ngx_string("error_page"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_2MORE,

      ngx_http_core_error_page,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("try_files"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,

      ngx_http_core_try_files,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("post_action"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, post_action),

      NULL },

    { ngx_string("error_log"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_core_error_log,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("open_file_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_core_open_file_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache),

      NULL },

    { ngx_string("open_file_cache_valid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_sec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),

      NULL },

    { ngx_string("open_file_cache_retest"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_sec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),

      &ngx_conf_deprecated_open_file_cache_retest },

    { ngx_string("open_file_cache_min_uses"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache_min_uses),

      NULL },

    { ngx_string("open_file_cache_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache_errors),

      NULL },

    { ngx_string("open_file_cache_events"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, open_file_cache_events),

      NULL },

    { ngx_string("resolver"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_core_resolver,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("resolver_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, resolver_timeout),

      NULL },

#if (NGX_HTTP_GZIP)

    { ngx_string("gzip_vary"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, gzip_vary),

      NULL },

    { ngx_string("gzip_http_version"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, gzip_http_version),

      &ngx_http_gzip_http_version },

    { ngx_string("gzip_proxied"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_core_loc_conf_t, gzip_proxied),

      &ngx_http_gzip_proxied_mask },

    { ngx_string("gzip_disable"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_gzip_disable,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

#endif

#if (NGX_HAVE_OPENAT)

    { ngx_string("disable_symlinks"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_disable_symlinks,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

#endif

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_core_module_ctx = {

    ngx_http_core_preconfiguration,        /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_core_create_main_conf,        /* create main configuration */

    ngx_http_core_init_main_conf,          /* init main configuration */

    ngx_http_core_create_srv_conf,         /* create server configuration */

    ngx_http_core_merge_srv_conf,          /* merge server configuration */

    ngx_http_core_create_loc_conf,         /* create location configuration */

    ngx_http_core_merge_loc_conf           /* merge location configuration */

};

‌ngx_module_t  ngx_http_core_module = {

    NGX_MODULE_V1,

    &ngx_http_core_module_ctx,             /* module context */

    ngx_http_core_commands,                /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌ngx_str_t  ngx_http_core_get_method = { 3, (u_char *) "GET " };

void

‌ngx_http_handler(ngx_http_request_t *r)

{

    ngx_http_core_main_conf_t  *cmcf;

    r->connection->log->action = NULL;

    r->connection->unexpected_eof = 0;

    if (!r->internal) {

        switch (r->headers_in.connection_type) {

        case 0:

            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);

            break;

        case NGX_HTTP_CONNECTION_CLOSE:

            r->keepalive = 0;

            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:

            r->keepalive = 1;

            break;

        }

        r->lingering_close = (r->headers_in.content_length_n > 0

                              || r->headers_in.chunked);

        r->phase_handler = 0;

    } else {

        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

        r->phase_handler = cmcf->phase_engine.server_rewrite_index;

    }

    r->valid_location = 1;

#if (NGX_HTTP_GZIP)

    r->gzip_tested = 0;

    r->gzip_ok = 0;

    r->gzip_vary = 0;

#endif

    r->write_event_handler = ngx_http_core_run_phases;

    ngx_http_core_run_phases(r);

}

void

‌ngx_http_core_run_phases(ngx_http_request_t *r)

{

    ngx_int_t                   rc;

    ngx_http_phase_handler_t   *ph;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;

    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {

            return;

        }

    }

}

ngx_int_t

‌ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)

{

    ngx_int_t  rc;

    /*

     * generic phase checker,

     * used by the post read and pre-access phases

     */

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "generic phase: %ui", r->phase_handler);

    rc = ph->handler(r);

    if (rc == NGX_OK) {

        r->phase_handler = ph->next;

        return NGX_AGAIN;

    }

    if (rc == NGX_DECLINED) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    if (rc == NGX_AGAIN || rc == NGX_DONE) {

        return NGX_OK;

    }

    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */

    ngx_http_finalize_request(r, rc);

    return NGX_OK;

}

ngx_int_t

‌ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)

{

    ngx_int_t  rc;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "rewrite phase: %ui", r->phase_handler);

    rc = ph->handler(r);

    if (rc == NGX_DECLINED) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    if (rc == NGX_DONE) {

        return NGX_OK;

    }

    /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_...  */

    ngx_http_finalize_request(r, rc);

    return NGX_OK;

}

ngx_int_t

‌ngx_http_core_find_config_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    u_char                    *p;

    size_t                     len;

    ngx_int_t                  rc;

    ngx_http_core_loc_conf_t  *clcf;

    r->content_handler = NULL;

    r->uri_changed = 0;

    rc = ngx_http_core_find_location(r);

    if (rc == NGX_ERROR) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_OK;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!r->internal && clcf->internal) {

        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);

        return NGX_OK;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "using configuration \"%s%V\"",

                   (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")),

                   &clcf->name);

    ngx_http_update_location_config(r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http cl:%O max:%O",

                   r->headers_in.content_length_n, clcf->client_max_body_size);

    if (r->headers_in.content_length_n != -1

        && !r->discard_body

        && clcf->client_max_body_size

        && clcf->client_max_body_size < r->headers_in.content_length_n)

    {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client intended to send too large body: %O bytes",

                      r->headers_in.content_length_n);

        r->expect_tested = 1;

        (void) ngx_http_discard_request_body(r);

        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);

        return NGX_OK;

    }

    if (rc == NGX_DONE) {

        ngx_http_clear_location(r);

        r->headers_out.location = ngx_list_push(&r->headers_out.headers);

        if (r->headers_out.location == NULL) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_OK;

        }

        /*

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

         * r->headers_out.location->key fields

         */

        if (r->args.len == 0) {

            r->headers_out.location->value = clcf->name;

        } else {

            len = clcf->name.len + 1 + r->args.len;

            p = ngx_pnalloc(r->pool, len);

            if (p == NULL) {

                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return NGX_OK;

            }

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

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

            p = ngx_cpymem(p, clcf->name.data, clcf->name.len);

            *p++ = '?';

            ngx_memcpy(p, r->args.data, r->args.len);

        }

        ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);

        return NGX_OK;

    }

    r->phase_handler++;

    return NGX_AGAIN;

}

ngx_int_t

‌ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    ngx_http_core_srv_conf_t  *cscf;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "post rewrite phase: %ui", r->phase_handler);

    if (!r->uri_changed) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "uri changes: %d", r->uri_changes);

    /*

     * gcc before 3.3 compiles the broken code for

     *     if (r->uri_changes-- == 0)

     * if the r->uri_changes is defined as

     *     unsigned  uri_changes:4

     */

    r->uri_changes--;

    if (r->uri_changes == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "rewrite or internal redirection cycle "

                      "while processing \"%V\"", &r->uri);

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_OK;

    }

    r->phase_handler = ph->next;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    r->loc_conf = cscf->ctx->loc_conf;

    return NGX_AGAIN;

}

ngx_int_t

‌ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)

{

    ngx_int_t                  rc;

    ngx_http_core_loc_conf_t  *clcf;

    if (r != r->main) {

        r->phase_handler = ph->next;

        return NGX_AGAIN;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "access phase: %ui", r->phase_handler);

    rc = ph->handler(r);

    if (rc == NGX_DECLINED) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    if (rc == NGX_AGAIN || rc == NGX_DONE) {

        return NGX_OK;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {

        if (rc == NGX_OK) {

            r->phase_handler++;

            return NGX_AGAIN;

        }

    } else {

        if (rc == NGX_OK) {

            r->access_code = 0;

            if (r->headers_out.www_authenticate) {

                r->headers_out.www_authenticate->hash = 0;

            }

            r->phase_handler = ph->next;

            return NGX_AGAIN;

        }

        if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {

            if (r->access_code != NGX_HTTP_UNAUTHORIZED) {

                r->access_code = rc;

            }

            r->phase_handler++;

            return NGX_AGAIN;

        }

    }

    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */

    ngx_http_finalize_request(r, rc);

    return NGX_OK;

}

ngx_int_t

‌ngx_http_core_post_access_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    ngx_int_t  access_code;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "post access phase: %ui", r->phase_handler);

    access_code = r->access_code;

    if (access_code) {

        if (access_code == NGX_HTTP_FORBIDDEN) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "access forbidden by rule");

        }

        r->access_code = 0;

        ngx_http_finalize_request(r, access_code);

        return NGX_OK;

    }

    r->phase_handler++;

    return NGX_AGAIN;

}

ngx_int_t

‌ngx_http_core_try_files_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    size_t                        len, root, alias, reserve, allocated;

    u_char                       *p, *name;

    ngx_str_t                     path, args;

    ngx_uint_t                    test_dir;

    ngx_http_try_file_t          *tf;

    ngx_open_file_info_t          of;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t      e;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_script_len_code_pt   lcode;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "try files phase: %ui", r->phase_handler);

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->try_files == NULL) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    allocated = 0;

    root = 0;

    name = NULL;

    /* suppress MSVC warning */

    path.data = NULL;

    tf = clcf->try_files;

    alias = clcf->alias;

    for ( ;; ) {

        if (tf->lengths) {

            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

            e.ip = tf->lengths->elts;

            e.request = r;

            /* 1 is for terminating '\0' as in static names */

            len = 1;

            while (*(uintptr_t *) e.ip) {

                lcode = *(ngx_http_script_len_code_pt *) e.ip;

                len += lcode(&e);

            }

        } else {

            len = tf->name.len;

        }

        if (!alias) {

            reserve = len > r->uri.len ? len - r->uri.len : 0;

        } else if (alias == NGX_MAX_SIZE_T_VALUE) {

            reserve = len;

        } else {

            reserve = len > r->uri.len - alias ? len - (r->uri.len - alias) : 0;

        }

        if (reserve > allocated || !allocated) {

            /* 16 bytes are preallocation */

            allocated = reserve + 16;

            if (ngx_http_map_uri_to_path(r, &path, &root, allocated) == NULL) {

                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return NGX_OK;

            }

            name = path.data + root;

         }

        if (tf->values == NULL) {

            /* tf->name.len includes the terminating '\0' */

            ngx_memcpy(name, tf->name.data, tf->name.len);

            path.len = (name + tf->name.len - 1) - path.data;

        } else {

            e.ip = tf->values->elts;

            e.pos = name;

            e.flushed = 1;

            while (*(uintptr_t *) e.ip) {

                code = *(ngx_http_script_code_pt *) e.ip;

                code((ngx_http_script_engine_t *) &e);

            }

            path.len = e.pos - path.data;

            *e.pos = '\0';

            if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) {

                ngx_memmove(name, name + alias, len - alias);

                path.len -= alias;

            }

        }

        test_dir = tf->test_dir;

        tf++;

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "trying to use %s: \"%s\" \"%s\"",

                       test_dir ? "dir" : "file", name, path.data);

        if (tf->lengths == NULL && tf->name.len == 0) {

            if (tf->code) {

                ngx_http_finalize_request(r, tf->code);

                return NGX_OK;

            }

            path.len -= root;

            path.data += root;

            if (path.data[0] == '@') {

                (void) ngx_http_named_location(r, &path);

            } else {

                ngx_http_split_args(r, &path, &args);

                (void) ngx_http_internal_redirect(r, &path, &args);

            }

            ngx_http_finalize_request(r, NGX_DONE);

            return NGX_OK;

        }

        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.test_only = 1;

        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) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_OK;

        }

        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)

            != NGX_OK)

        {

            if (of.err != NGX_ENOENT

                && of.err != NGX_ENOTDIR

                && of.err != NGX_ENAMETOOLONG)

            {

                ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,

                              "%s \"%s\" failed", of.failed, path.data);

            }

            continue;

        }

        if (of.is_dir && !test_dir) {

            continue;

        }

        path.len -= root;

        path.data += root;

        if (!alias) {

            r->uri = path;

        } else if (alias == NGX_MAX_SIZE_T_VALUE) {

            if (!test_dir) {

                r->uri = path;

                r->add_uri_to_alias = 1;

            }

        } else {

            r->uri.len = alias + path.len;

            r->uri.data = ngx_pnalloc(r->pool, r->uri.len);

            if (r->uri.data == NULL) {

                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return NGX_OK;

            }

            p = ngx_copy(r->uri.data, clcf->name.data, alias);

            ngx_memcpy(p, name, path.len);

        }

        ngx_http_set_exten(r);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "try file uri: \"%V\"", &r->uri);

        r->phase_handler++;

        return NGX_AGAIN;

    }

    /* not reached */

}

ngx_int_t

‌ngx_http_core_content_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    size_t     root;

    ngx_int_t  rc;

    ngx_str_t  path;

    if (r->content_handler) {

        r->write_event_handler = ngx_http_request_empty_handler;

        ngx_http_finalize_request(r, r->content_handler(r));

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "content phase: %ui", r->phase_handler);

    rc = ph->handler(r);

    if (rc != NGX_DECLINED) {

        ngx_http_finalize_request(r, rc);

        return NGX_OK;

    }

    /* rc == NGX_DECLINED */

    ph++;

    if (ph->checker) {

        r->phase_handler++;

        return NGX_AGAIN;

    }

    /* no content handler was found */

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

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "directory index of \"%s\" is forbidden", path.data);

        }

        ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");

    ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);

    return NGX_OK;

}

void

‌ngx_http_update_location_config(ngx_http_request_t *r)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->method & clcf->limit_except) {

        r->loc_conf = clcf->limit_except_loc_conf;

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    }

    if (r == r->main) {

        ngx_http_set_connection_log(r->connection, clcf->error_log);

    }

    if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {

        r->connection->sendfile = 1;

    } else {

        r->connection->sendfile = 0;

    }

    if (clcf->client_body_in_file_only) {

        r->request_body_in_file_only = 1;

        r->request_body_in_persistent_file = 1;

        r->request_body_in_clean_file =

            clcf->client_body_in_file_only == NGX_HTTP_REQUEST_BODY_FILE_CLEAN;

        r->request_body_file_log_level = NGX_LOG_NOTICE;

    } else {

        r->request_body_file_log_level = NGX_LOG_WARN;

    }

    r->request_body_in_single_buf = clcf->client_body_in_single_buffer;

    if (r->keepalive) {

        if (clcf->keepalive_timeout == 0) {

            r->keepalive = 0;

        } else if (r->connection->requests >= clcf->keepalive_requests) {

            r->keepalive = 0;

        } else if (r->headers_in.msie6

                   && r->method == NGX_HTTP_POST

                   && (clcf->keepalive_disable

                       & NGX_HTTP_KEEPALIVE_DISABLE_MSIE6))

        {

            /*

             * MSIE may wait for some time if an response for

             * a POST request was sent over a keepalive connection

             */

            r->keepalive = 0;

        } else if (r->headers_in.safari

                   && (clcf->keepalive_disable

                       & NGX_HTTP_KEEPALIVE_DISABLE_SAFARI))

        {

            /*

             * Safari may send a POST request to a closed keepalive

             * connection and may stall for some time, see

             *     https://bugs.webkit.org/show_bug.cgi?id=5760

             */

            r->keepalive = 0;

        }

    }

    if (!clcf->tcp_nopush) {

        /* disable TCP_NOPUSH/TCP_CORK use */

        r->connection->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;

    }

    if (r->limit_rate == 0) {

        r->limit_rate = clcf->limit_rate;

    }

    if (clcf->handler) {

        r->content_handler = clcf->handler;

    }

}

/*

* NGX_OK       - exact or regex match

* NGX_DONE     - auto redirect

* NGX_AGAIN    - inclusive match

* NGX_ERROR    - regex error

* NGX_DECLINED - no match

*/

static ngx_int_t

‌ngx_http_core_find_location(ngx_http_request_t *r)

{

    ngx_int_t                  rc;

    ngx_http_core_loc_conf_t  *pclcf;

#if (NGX_PCRE)

    ngx_int_t                  n;

    ngx_uint_t                 noregex;

    ngx_http_core_loc_conf_t  *clcf, **clcfp;

    noregex = 0;

#endif

    pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    rc = ngx_http_core_find_static_location(r, pclcf->static_locations);

    if (rc == NGX_AGAIN) {

#if (NGX_PCRE)

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        noregex = clcf->noregex;

#endif

        /* look up nested locations */

        rc = ngx_http_core_find_location(r);

    }

    if (rc == NGX_OK || rc == NGX_DONE) {

        return rc;

    }

    /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */

#if (NGX_PCRE)

    if (noregex == 0 && pclcf->regex_locations) {

        for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "test location: ~ \"%V\"", &(*clcfp)->name);

            n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);

            if (n == NGX_OK) {

                r->loc_conf = (*clcfp)->loc_conf;

                /* look up nested locations */

                rc = ngx_http_core_find_location(r);

                return (rc == NGX_ERROR) ? rc : NGX_OK;

            }

            if (n == NGX_DECLINED) {

                continue;

            }

            return NGX_ERROR;

        }

    }

#endif

    return rc;

}

/*

* NGX_OK       - exact match

* NGX_DONE     - auto redirect

* NGX_AGAIN    - inclusive match

* NGX_DECLINED - no match

*/

static ngx_int_t

‌ngx_http_core_find_static_location(ngx_http_request_t *r,

    ngx_http_location_tree_node_t *node)

{

    u_char     *uri;

    size_t      len, n;

    ngx_int_t   rc, rv;

    len = r->uri.len;

    uri = r->uri.data;

    rv = NGX_DECLINED;

    for ( ;; ) {

        if (node == NULL) {

            return rv;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "test location: \"%*s\"", node->len, node->name);

        n = (len <= (size_t) node->len) ? len : node->len;

        rc = ngx_filename_cmp(uri, node->name, n);

        if (rc != 0) {

            node = (rc < 0) ? node->left : node->right;

            continue;

        }

        if (len > (size_t) node->len) {

            if (node->inclusive) {

                r->loc_conf = node->inclusive->loc_conf;

                rv = NGX_AGAIN;

                node = node->tree;

                uri += n;

                len -= n;

                continue;

            }

            /* exact only */

            node = node->right;

            continue;

        }

        if (len == (size_t) node->len) {

            if (node->exact) {

                r->loc_conf = node->exact->loc_conf;

                return NGX_OK;

            } else {

                r->loc_conf = node->inclusive->loc_conf;

                return NGX_AGAIN;

            }

        }

        /* len < node->len */

        if (len + 1 == (size_t) node->len && node->auto_redirect) {

            r->loc_conf = (node->exact) ? node->exact->loc_conf:

                                          node->inclusive->loc_conf;

            rv = NGX_DONE;

        }

        node = node->left;

    }

}

void *

‌ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)

{

    u_char      c, *lowcase;

    size_t      len;

    ngx_uint_t  i, hash;

    if (types_hash->size == 0) {

        return (void *) 4;

    }

    if (r->headers_out.content_type.len == 0) {

        return NULL;

    }

    len = r->headers_out.content_type_len;

    if (r->headers_out.content_type_lowcase == NULL) {

        lowcase = ngx_pnalloc(r->pool, len);

        if (lowcase == NULL) {

            return NULL;

        }

        r->headers_out.content_type_lowcase = lowcase;

        hash = 0;

        for (i = 0; i < len; i++) {

            c = ngx_tolower(r->headers_out.content_type.data[i]);

            hash = ngx_hash(hash, c);

            lowcase[i] = c;

        }

        r->headers_out.content_type_hash = hash;

    }

    return ngx_hash_find(types_hash, r->headers_out.content_type_hash,

                         r->headers_out.content_type_lowcase, len);

}

ngx_int_t

‌ngx_http_set_content_type(ngx_http_request_t *r)

{

    u_char                     c, *exten;

    ngx_str_t                 *type;

    ngx_uint_t                 i, hash;

    ngx_http_core_loc_conf_t  *clcf;

    if (r->headers_out.content_type.len) {

        return NGX_OK;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->exten.len) {

        hash = 0;

        for (i = 0; i < r->exten.len; i++) {

            c = r->exten.data[i];

            if (c >= 'A' && c <= 'Z') {

                exten = ngx_pnalloc(r->pool, r->exten.len);

                if (exten == NULL) {

                    return NGX_ERROR;

                }

                hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);

                r->exten.data = exten;

                break;

            }

            hash = ngx_hash(hash, c);

        }

        type = ngx_hash_find(&clcf->types_hash, hash,

                             r->exten.data, r->exten.len);

        if (type) {

            r->headers_out.content_type_len = type->len;

            r->headers_out.content_type = *type;

            return NGX_OK;

        }

    }

    r->headers_out.content_type_len = clcf->default_type.len;

    r->headers_out.content_type = clcf->default_type;

    return NGX_OK;

}

void

‌ngx_http_set_exten(ngx_http_request_t *r)

{

    ngx_int_t  i;

    ngx_str_null(&r->exten);

    for (i = r->uri.len - 1; i > 1; i--) {

        if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') {

            r->exten.len = r->uri.len - i - 1;

            r->exten.data = &r->uri.data[i + 1];

            return;

        } else if (r->uri.data[i] == '/') {

            return;

        }

    }

    return;

}

ngx_int_t

‌ngx_http_set_etag(ngx_http_request_t *r)

{

    ngx_table_elt_t           *etag;

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->etag) {

        return NGX_OK;

    }

    etag = ngx_list_push(&r->headers_out.headers);

    if (etag == NULL) {

        return NGX_ERROR;

    }

    etag->hash = 1;

    ngx_str_set(&etag->key, "ETag");

    etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);

    if (etag->value.data == NULL) {

        etag->hash = 0;

        return NGX_ERROR;

    }

    etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",

                                  r->headers_out.last_modified_time,

                                  r->headers_out.content_length_n)

                      - etag->value.data;

    r->headers_out.etag = etag;

    return NGX_OK;

}

void

‌ngx_http_weak_etag(ngx_http_request_t *r)

{

    size_t            len;

    u_char           *p;

    ngx_table_elt_t  *etag;

    etag = r->headers_out.etag;

    if (etag == NULL) {

        return;

    }

    if (etag->value.len > 2

        && etag->value.data[0] == 'W'

        && etag->value.data[1] == '/')

    {

        return;

    }

    if (etag->value.len < 1 || etag->value.data[0] != '"') {

        r->headers_out.etag->hash = 0;

        r->headers_out.etag = NULL;

        return;

    }

    p = ngx_pnalloc(r->pool, etag->value.len + 2);

    if (p == NULL) {

        r->headers_out.etag->hash = 0;

        r->headers_out.etag = NULL;

        return;

    }

    len = ngx_sprintf(p, "W/%V", &etag->value) - p;

    etag->value.data = p;

    etag->value.len = len;

}

ngx_int_t

‌ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,

    ngx_str_t *ct, ngx_http_complex_value_t *cv)

{

    ngx_int_t     rc;

    ngx_str_t     val;

    ngx_buf_t    *b;

    ngx_chain_t   out;

    if (ngx_http_discard_request_body(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    r->headers_out.status = status;

    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (status == NGX_HTTP_MOVED_PERMANENTLY

        || status == NGX_HTTP_MOVED_TEMPORARILY

        || status == NGX_HTTP_SEE_OTHER

        || status == NGX_HTTP_TEMPORARY_REDIRECT)

    {

        ngx_http_clear_location(r);

        r->headers_out.location = ngx_list_push(&r->headers_out.headers);

        if (r->headers_out.location == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        r->headers_out.location->hash = 1;

        ngx_str_set(&r->headers_out.location->key, "Location");

        r->headers_out.location->value = val;

        return status;

    }

    r->headers_out.content_length_n = val.len;

    if (ct) {

        r->headers_out.content_type_len = ct->len;

        r->headers_out.content_type = *ct;

    } else {

        if (ngx_http_set_content_type(r) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    if (r->method == NGX_HTTP_HEAD || (r != r->main && val.len == 0)) {

        return ngx_http_send_header(r);

    }

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

    if (b == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    b->pos = val.data;

    b->last = val.data + val.len;

    b->memory = val.len ? 1 : 0;

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

    b->last_in_chain = 1;

    out.buf = b;

    out.next = NULL;

    rc = ngx_http_send_header(r);

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

        return rc;

    }

    return ngx_http_output_filter(r, &out);

}

ngx_int_t

‌ngx_http_send_header(ngx_http_request_t *r)

{

    if (r->post_action) {

        return NGX_OK;

    }

    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;

    }

    return ngx_http_top_header_filter(r);

}

ngx_int_t

‌ngx_http_output_filter(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);

    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;

}

u_char *

‌ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,

    size_t *root_length, size_t reserved)

{

    u_char                    *last;

    size_t                     alias;

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    alias = clcf->alias;

    if (alias && !r->valid_location) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "\"alias\" cannot be used in location \"%V\" "

                      "where URI was rewritten", &clcf->name);

        return NULL;

    }

    if (clcf->root_lengths == NULL) {

        *root_length = clcf->root.len;

        path->len = clcf->root.len + reserved + r->uri.len - alias + 1;

        path->data = ngx_pnalloc(r->pool, path->len);

        if (path->data == NULL) {

            return NULL;

        }

        last = ngx_copy(path->data, clcf->root.data, clcf->root.len);

    } else {

        if (alias == NGX_MAX_SIZE_T_VALUE) {

            reserved += r->add_uri_to_alias ? r->uri.len + 1 : 1;

        } else {

            reserved += r->uri.len - alias + 1;

        }

        if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,

                                clcf->root_values->elts)

            == NULL)

        {

            return NULL;

        }

        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, path)

            != NGX_OK)

        {

            return NULL;

        }

        *root_length = path->len - reserved;

        last = path->data + *root_length;

        if (alias == NGX_MAX_SIZE_T_VALUE) {

            if (!r->add_uri_to_alias) {

                *last = '\0';

                return last;

            }

            alias = 0;

        }

    }

    last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);

    return last;

}

ngx_int_t

‌ngx_http_auth_basic_user(ngx_http_request_t *r)

{

    ngx_str_t   auth, encoded;

    ngx_uint_t  len;

    if (r->headers_in.user.len == 0 && r->headers_in.user.data != NULL) {

        return NGX_DECLINED;

    }

    if (r->headers_in.authorization == NULL) {

        r->headers_in.user.data = (u_char *) "";

        return NGX_DECLINED;

    }

    encoded = r->headers_in.authorization->value;

    if (encoded.len < sizeof("Basic ") - 1

        || ngx_strncasecmp(encoded.data, (u_char *) "Basic ",

                           sizeof("Basic ") - 1)

           != 0)

    {

        r->headers_in.user.data = (u_char *) "";

        return NGX_DECLINED;

    }

    encoded.len -= sizeof("Basic ") - 1;

    encoded.data += sizeof("Basic ") - 1;

    while (encoded.len && encoded.data[0] == ' ') {

        encoded.len--;

        encoded.data++;

    }

    if (encoded.len == 0) {

        r->headers_in.user.data = (u_char *) "";

        return NGX_DECLINED;

    }

    auth.len = ngx_base64_decoded_length(encoded.len);

    auth.data = ngx_pnalloc(r->pool, auth.len + 1);

    if (auth.data == NULL) {

        return NGX_ERROR;

    }

    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {

        r->headers_in.user.data = (u_char *) "";

        return NGX_DECLINED;

    }

    auth.data[auth.len] = '\0';

    for (len = 0; len < auth.len; len++) {

        if (auth.data[len] == ':') {

            break;

        }

    }

    if (len == 0 || len == auth.len) {

        r->headers_in.user.data = (u_char *) "";

        return NGX_DECLINED;

    }

    r->headers_in.user.len = len;

    r->headers_in.user.data = auth.data;

    r->headers_in.passwd.len = auth.len - len - 1;

    r->headers_in.passwd.data = &auth.data[len + 1];

    return NGX_OK;

}

#if (NGX_HTTP_GZIP)

ngx_int_t

‌ngx_http_gzip_ok(ngx_http_request_t *r)

{

    time_t                     date, expires;

    ngx_uint_t                 p;

    ngx_array_t               *cc;

    ngx_table_elt_t           *e, *d, *ae;

    ngx_http_core_loc_conf_t  *clcf;

    r->gzip_tested = 1;

    if (r != r->main) {

        return NGX_DECLINED;

    }

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        r->gzip_ok = 1;

        return NGX_OK;

    }

#endif

    ae = r->headers_in.accept_encoding;

    if (ae == NULL) {

        return NGX_DECLINED;

    }

    if (ae->value.len < sizeof("gzip") - 1) {

        return NGX_DECLINED;

    }

    /*

     * test first for the most common case "gzip,...":

     *   MSIE:    "gzip, deflate"

     *   Firefox: "gzip,deflate"

     *   Chrome:  "gzip,deflate,sdch"

     *   Safari:  "gzip, deflate"

     *   Opera:   "gzip, deflate"

     */

    if (ngx_memcmp(ae->value.data, "gzip,", 5) != 0

        && ngx_http_gzip_accept_encoding(&ae->value) != NGX_OK)

    {

        return NGX_DECLINED;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->headers_in.msie6 && clcf->gzip_disable_msie6) {

        return NGX_DECLINED;

    }

    if (r->http_version < clcf->gzip_http_version) {

        return NGX_DECLINED;

    }

    if (r->headers_in.via == NULL) {

        goto ok;

    }

    p = clcf->gzip_proxied;

    if (p & NGX_HTTP_GZIP_PROXIED_OFF) {

        return NGX_DECLINED;

    }

    if (p & NGX_HTTP_GZIP_PROXIED_ANY) {

        goto ok;

    }

    if (r->headers_in.authorization && (p & NGX_HTTP_GZIP_PROXIED_AUTH)) {

        goto ok;

    }

    e = r->headers_out.expires;

    if (e) {

        if (!(p & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {

            return NGX_DECLINED;

        }

        expires = ngx_http_parse_time(e->value.data, e->value.len);

        if (expires == NGX_ERROR) {

            return NGX_DECLINED;

        }

        d = r->headers_out.date;

        if (d) {

            date = ngx_http_parse_time(d->value.data, d->value.len);

            if (date == NGX_ERROR) {

                return NGX_DECLINED;

            }

        } else {

            date = ngx_time();

        }

        if (expires < date) {

            goto ok;

        }

        return NGX_DECLINED;

    }

    cc = &r->headers_out.cache_control;

    if (cc->elts) {

        if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE)

            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_cache,

                                                 NULL)

               >= 0)

        {

            goto ok;

        }

        if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE)

            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_store,

                                                 NULL)

               >= 0)

        {

            goto ok;

        }

        if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE)

            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_private,

                                                 NULL)

               >= 0)

        {

            goto ok;

        }

        return NGX_DECLINED;

    }

    if ((p & NGX_HTTP_GZIP_PROXIED_NO_LM) && r->headers_out.last_modified) {

        return NGX_DECLINED;

    }

    if ((p & NGX_HTTP_GZIP_PROXIED_NO_ETAG) && r->headers_out.etag) {

        return NGX_DECLINED;

    }

ok:

#if (NGX_PCRE)

    if (clcf->gzip_disable && r->headers_in.user_agent) {

        if (ngx_regex_exec_array(clcf->gzip_disable,

                                 &r->headers_in.user_agent->value,

                                 r->connection->log)

            != NGX_DECLINED)

        {

            return NGX_DECLINED;

        }

    }

#endif

    r->gzip_ok = 1;

    return NGX_OK;

}

/*

* gzip is enabled for the following quantities:

*     "gzip; q=0.001" ... "gzip; q=1.000"

* gzip is disabled for the following quantities:

*     "gzip; q=0" ... "gzip; q=0.000", and for any invalid cases

*/

static ngx_int_t

‌ngx_http_gzip_accept_encoding(ngx_str_t *ae)

{

    u_char  *p, *start, *last;

    start = ae->data;

    last = start + ae->len;

    for ( ;; ) {

        p = ngx_strcasestrn(start, "gzip", 4 - 1);

        if (p == NULL) {

            return NGX_DECLINED;

        }

        if (p == start || (*(p - 1) == ',' || *(p - 1) == ' ')) {

            break;

        }

        start = p + 4;

    }

    p += 4;

    while (p < last) {

        switch (*p++) {

        case ',':

            return NGX_OK;

        case ';':

            goto quantity;

        case ' ':

            continue;

        default:

            return NGX_DECLINED;

        }

    }

    return NGX_OK;

quantity:

    while (p < last) {

        switch (*p++) {

        case 'q':

        case 'Q':

            goto equal;

        case ' ':

            continue;

        default:

            return NGX_DECLINED;

        }

    }

    return NGX_OK;

equal:

    if (p + 2 > last || *p++ != '=') {

        return NGX_DECLINED;

    }

    if (ngx_http_gzip_quantity(p, last) == 0) {

        return NGX_DECLINED;

    }

    return NGX_OK;

}

static ngx_uint_t

‌ngx_http_gzip_quantity(u_char *p, u_char *last)

{

    u_char      c;

    ngx_uint_t  n, q;

    c = *p++;

    if (c != '0' && c != '1') {

        return 0;

    }

    q = (c - '0') * 100;

    if (p == last) {

        return q;

    }

    c = *p++;

    if (c == ',' || c == ' ') {

        return q;

    }

    if (c != '.') {

        return 0;

    }

    n = 0;

    while (p < last) {

        c = *p++;

        if (c == ',' || c == ' ') {

            break;

        }

        if (c >= '0' && c <= '9') {

            q += c - '0';

            n++;

            continue;

        }

        return 0;

    }

    if (q > 100 || n > 3) {

        return 0;

    }

    return q;

}

#endif

ngx_int_t

‌ngx_http_subrequest(ngx_http_request_t *r,

    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,

    ngx_http_post_subrequest_t *ps, ngx_uint_t flags)

{

    ngx_time_t                    *tp;

    ngx_connection_t              *c;

    ngx_http_request_t            *sr;

    ngx_http_core_srv_conf_t      *cscf;

    ngx_http_postponed_request_t  *pr, *p;

    r->main->subrequests--;

    if (r->main->subrequests == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "subrequests cycle while processing \"%V\"", uri);

        r->main->subrequests = 1;

        return NGX_ERROR;

    }

    sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));

    if (sr == NULL) {

        return NGX_ERROR;

    }

    sr->signature = NGX_HTTP_MODULE;

    c = r->connection;

    sr->connection = c;

    sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);

    if (sr->ctx == NULL) {

        return NGX_ERROR;

    }

    if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,

                      sizeof(ngx_table_elt_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    sr->main_conf = cscf->ctx->main_conf;

    sr->srv_conf = cscf->ctx->srv_conf;

    sr->loc_conf = cscf->ctx->loc_conf;

    sr->pool = r->pool;

    sr->headers_in = r->headers_in;

    ngx_http_clear_content_length(sr);

    ngx_http_clear_accept_ranges(sr);

    ngx_http_clear_last_modified(sr);

    sr->request_body = r->request_body;

#if (NGX_HTTP_SPDY)

    sr->spdy_stream = r->spdy_stream;

#endif

    sr->method = NGX_HTTP_GET;

    sr->http_version = r->http_version;

    sr->request_line = r->request_line;

    sr->uri = *uri;

    if (args) {

        sr->args = *args;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http subrequest \"%V?%V\"", uri, &sr->args);

    sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;

    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;

    sr->unparsed_uri = r->unparsed_uri;

    sr->method_name = ngx_http_core_get_method;

    sr->http_protocol = r->http_protocol;

    ngx_http_set_exten(sr);

    sr->main = r->main;

    sr->parent = r;

    sr->post_subrequest = ps;

    sr->read_event_handler = ngx_http_request_empty_handler;

    sr->write_event_handler = ngx_http_handler;

    if (c->data == r && r->postponed == NULL) {

        c->data = sr;

    }

    sr->variables = r->variables;

    sr->log_handler = r->log_handler;

    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));

    if (pr == NULL) {

        return NGX_ERROR;

    }

    pr->request = sr;

    pr->out = NULL;

    pr->next = NULL;

    if (r->postponed) {

        for (p = r->postponed; p->next; p = p->next) { /* void */ }

        p->next = pr;

    } else {

        r->postponed = pr;

    }

    sr->internal = 1;

    sr->discard_body = r->discard_body;

    sr->expect_tested = 1;

    sr->main_filter_need_in_memory = r->main_filter_need_in_memory;

    sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;

    tp = ngx_timeofday();

    sr->start_sec = tp->sec;

    sr->start_msec = tp->msec;

    r->main->count++;

    *psr = sr;

    return ngx_http_post_request(sr, NULL);

}

ngx_int_t

‌ngx_http_internal_redirect(ngx_http_request_t *r,

    ngx_str_t *uri, ngx_str_t *args)

{

    ngx_http_core_srv_conf_t  *cscf;

    r->uri_changes--;

    if (r->uri_changes == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "rewrite or internal redirection cycle "

                      "while internally redirecting to \"%V\"", uri);

        r->main->count++;

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_DONE;

    }

    r->uri = *uri;

    if (args) {

        r->args = *args;

    } else {

        ngx_str_null(&r->args);

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "internal redirect: \"%V?%V\"", uri, &r->args);

    ngx_http_set_exten(r);

    /* clear the modules contexts */

    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    r->loc_conf = cscf->ctx->loc_conf;

    ngx_http_update_location_config(r);

#if (NGX_HTTP_CACHE)

    r->cache = NULL;

#endif

    r->internal = 1;

    r->valid_unparsed_uri = 0;

    r->add_uri_to_alias = 0;

    r->main->count++;

    ngx_http_handler(r);

    return NGX_DONE;

}

ngx_int_t

‌ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)

{

    ngx_http_core_srv_conf_t    *cscf;

    ngx_http_core_loc_conf_t   **clcfp;

    ngx_http_core_main_conf_t   *cmcf;

    r->main->count++;

    r->uri_changes--;

    if (r->uri_changes == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "rewrite or internal redirection cycle "

                      "while redirect to named location \"%V\"", name);

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_DONE;

    }

    if (r->uri.len == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "empty URI in redirect to named location \"%V\"", name);

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_DONE;

    }

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    if (cscf->named_locations) {

        for (clcfp = cscf->named_locations; *clcfp; clcfp++) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "test location: \"%V\"", &(*clcfp)->name);

            if (name->len != (*clcfp)->name.len

                || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)

            {

                continue;

            }

            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "using location: %V \"%V?%V\"",

                           name, &r->uri, &r->args);

            r->internal = 1;

            r->content_handler = NULL;

            r->uri_changed = 0;

            r->loc_conf = (*clcfp)->loc_conf;

            /* clear the modules contexts */

            ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);

            ngx_http_update_location_config(r);

            cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

            r->phase_handler = cmcf->phase_engine.location_rewrite_index;

            r->write_event_handler = ngx_http_core_run_phases;

            ngx_http_core_run_phases(r);

            return NGX_DONE;

        }

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "could not find named location \"%V\"", name);

    ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

    return NGX_DONE;

}

ngx_http_cleanup_t *

‌ngx_http_cleanup_add(ngx_http_request_t *r, size_t size)

{

    ngx_http_cleanup_t  *cln;

    r = r->main;

    cln = ngx_palloc(r->pool, sizeof(ngx_http_cleanup_t));

    if (cln == NULL) {

        return NULL;

    }

    if (size) {

        cln->data = ngx_palloc(r->pool, size);

        if (cln->data == NULL) {

            return NULL;

        }

    } else {

        cln->data = NULL;

    }

    cln->handler = NULL;

    cln->next = r->cleanup;

    r->cleanup = cln;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http cleanup add: %p", cln);

    return cln;

}

ngx_int_t

‌ngx_http_set_disable_symlinks(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of)

{

#if (NGX_HAVE_OPENAT)

    u_char     *p;

    ngx_str_t   from;

    of->disable_symlinks = clcf->disable_symlinks;

    if (clcf->disable_symlinks_from == NULL) {

        return NGX_OK;

    }

    if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (from.len == 0

        || from.len > path->len

        || ngx_memcmp(path->data, from.data, from.len) != 0)

    {

        return NGX_OK;

    }

    if (from.len == path->len) {

        of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;

        return NGX_OK;

    }

    p = path->data + from.len;

    if (*p == '/') {

        of->disable_symlinks_from = from.len;

        return NGX_OK;

    }

    p--;

    if (*p == '/') {

        of->disable_symlinks_from = from.len - 1;

    }

#endif

    return NGX_OK;

}

ngx_int_t

‌ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,

    ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,

    int recursive)

{

    ngx_int_t          rc;

    ngx_uint_t         i, found;

    ngx_table_elt_t  **h;

    if (headers == NULL) {

        return ngx_http_get_forwarded_addr_internal(r, addr, value->data,

                                                    value->len, proxies,

                                                    recursive);

    }

    i = headers->nelts;

    h = headers->elts;

    rc = NGX_DECLINED;

    found = 0;

    while (i-- > 0) {

        rc = ngx_http_get_forwarded_addr_internal(r, addr, h[i]->value.data,

                                                  h[i]->value.len, proxies,

                                                  recursive);

        if (!recursive) {

            break;

        }

        if (rc == NGX_DECLINED && found) {

            rc = NGX_DONE;

            break;

        }

        if (rc != NGX_OK) {

            break;

        }

        found = 1;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr,

    u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive)

{

    u_char           *p;

    in_addr_t         inaddr;

    ngx_int_t         rc;

    ngx_addr_t        paddr;

    ngx_cidr_t       *cidr;

    ngx_uint_t        family, i;

#if (NGX_HAVE_INET6)

    ngx_uint_t        n;

    struct in6_addr  *inaddr6;

#endif

#if (NGX_SUPPRESS_WARN)

    inaddr = 0;

#if (NGX_HAVE_INET6)

    inaddr6 = NULL;

#endif

#endif

    family = addr->sockaddr->sa_family;

    if (family == AF_INET) {

        inaddr = ((struct sockaddr_in *) addr->sockaddr)->sin_addr.s_addr;

    }

#if (NGX_HAVE_INET6)

    else if (family == AF_INET6) {

        inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr;

        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {

            family = AF_INET;

            p = inaddr6->s6_addr;

            inaddr = p[12] << 24;

            inaddr += p[13] << 16;

            inaddr += p[14] << 8;

            inaddr += p[15];

            inaddr = htonl(inaddr);

        }

    }

#endif

    for (cidr = proxies->elts, i = 0; i < proxies->nelts; i++) {

        if (cidr[i].family != family) {

            goto next;

        }

        switch (family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            for (n = 0; n < 16; n++) {

                if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n])

                    != cidr[i].u.in6.addr.s6_addr[n])

                {

                    goto next;

                }

            }

            break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        case AF_UNIX:

            break;

#endif

        default: /* AF_INET */

            if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) {

                goto next;

            }

            break;

        }

        for (p = xff + xfflen - 1; p > xff; p--, xfflen--) {

            if (*p != ' ' && *p != ',') {

                break;

            }

        }

        for ( /* void */ ; p > xff; p--) {

            if (*p == ' ' || *p == ',') {

                p++;

                break;

            }

        }

        if (ngx_parse_addr(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) {

            return NGX_DECLINED;

        }

        *addr = paddr;

        if (recursive && p > xff) {

            rc = ngx_http_get_forwarded_addr_internal(r, addr, xff, p - 1 - xff,

                                                      proxies, 1);

            if (rc == NGX_DECLINED) {

                return NGX_DONE;

            }

            /* rc == NGX_OK || rc == NGX_DONE  */

            return rc;

        }

        return NGX_OK;

    next:

        continue;

    }

    return NGX_DECLINED;

}

static char *

‌ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)

{

    char                        *rv;

    void                        *mconf;

    ngx_uint_t                   i;

    ngx_conf_t                   pcf;

    ngx_http_module_t           *module;

    struct sockaddr_in          *sin;

    ngx_http_conf_ctx_t         *ctx, *http_ctx;

    ngx_http_listen_opt_t        lsopt;

    ngx_http_core_srv_conf_t    *cscf, **cscfp;

    ngx_http_core_main_conf_t   *cmcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    http_ctx = cf->ctx;

    ctx->main_conf = http_ctx->main_conf;

    /* the server{}'s srv_conf */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->srv_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /* the server{}'s loc_conf */

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->create_srv_conf) {

            mconf = module->create_srv_conf(cf);

            if (mconf == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;

        }

        if (module->create_loc_conf) {

            mconf = module->create_loc_conf(cf);

            if (mconf == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;

        }

    }

    /* the server configuration context */

    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];

    cscf->ctx = ctx;

    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

    cscfp = ngx_array_push(&cmcf->servers);

    if (cscfp == NULL) {

        return NGX_CONF_ERROR;

    }

    *cscfp = cscf;

    /* parse inside server{} */

    pcf = *cf;

    cf->ctx = ctx;

    cf->cmd_type = NGX_HTTP_SRV_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv == NGX_CONF_OK && !cscf->listen) {

        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

        sin = &lsopt.u.sockaddr_in;

        sin->sin_family = AF_INET;

#if (NGX_WIN32)

        sin->sin_port = htons(80);

#else

        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);

#endif

        sin->sin_addr.s_addr = INADDR_ANY;

        lsopt.socklen = sizeof(struct sockaddr_in);

        lsopt.backlog = NGX_LISTEN_BACKLOG;

        lsopt.rcvbuf = -1;

        lsopt.sndbuf = -1;

#if (NGX_HAVE_SETFIB)

        lsopt.setfib = -1;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

        lsopt.fastopen = -1;

#endif

        lsopt.wildcard = 1;

        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr,

                             NGX_SOCKADDR_STRLEN, 1);

        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return rv;

}

static char *

‌ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)

{

    char                      *rv;

    u_char                    *mod;

    size_t                     len;

    ngx_str_t                 *value, *name;

    ngx_uint_t                 i;

    ngx_conf_t                 save;

    ngx_http_module_t         *module;

    ngx_http_conf_ctx_t       *ctx, *pctx;

    ngx_http_core_loc_conf_t  *clcf, *pclcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    pctx = cf->ctx;

    ctx->main_conf = pctx->main_conf;

    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->create_loc_conf) {

            ctx->loc_conf[ngx_modules[i]->ctx_index] =

                                                   module->create_loc_conf(cf);

            if (ctx->loc_conf[ngx_modules[i]->ctx_index] == NULL) {

                 return NGX_CONF_ERROR;

            }

        }

    }

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

    clcf->loc_conf = ctx->loc_conf;

    value = cf->args->elts;

    if (cf->args->nelts == 3) {

        len = value[1].len;

        mod = value[1].data;

        name = &value[2];

        if (len == 1 && mod[0] == '=') {

            clcf->name = *name;

            clcf->exact_match = 1;

        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {

            clcf->name = *name;

            clcf->noregex = 1;

        } else if (len == 1 && mod[0] == '~') {

            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {

            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid location modifier \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

    } else {

        name = &value[1];

        if (name->data[0] == '=') {

            clcf->name.len = name->len - 1;

            clcf->name.data = name->data + 1;

            clcf->exact_match = 1;

        } else if (name->data[0] == '^' && name->data[1] == '~') {

            clcf->name.len = name->len - 2;

            clcf->name.data = name->data + 2;

            clcf->noregex = 1;

        } else if (name->data[0] == '~') {

            name->len--;

            name->data++;

            if (name->data[0] == '*') {

                name->len--;

                name->data++;

                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {

                    return NGX_CONF_ERROR;

                }

            } else {

                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {

                    return NGX_CONF_ERROR;

                }

            }

        } else {

            clcf->name = *name;

            if (name->data[0] == '@') {

                clcf->named = 1;

            }

        }

    }

    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

    if (pclcf->name.len) {

        /* nested location */

#if 0

        clcf->prev_location = pclcf;

#endif

        if (pclcf->exact_match) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "location \"%V\" cannot be inside "

                               "the exact location \"%V\"",

                               &clcf->name, &pclcf->name);

            return NGX_CONF_ERROR;

        }

        if (pclcf->named) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "location \"%V\" cannot be inside "

                               "the named location \"%V\"",

                               &clcf->name, &pclcf->name);

            return NGX_CONF_ERROR;

        }

        if (clcf->named) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "named location \"%V\" can be "

                               "on the server level only",

                               &clcf->name);

            return NGX_CONF_ERROR;

        }

        len = pclcf->name.len;

#if (NGX_PCRE)

        if (clcf->regex == NULL

            && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)

#else

        if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)

#endif

        {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "location \"%V\" is outside location \"%V\"",

                               &clcf->name, &pclcf->name);

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    save = *cf;

    cf->ctx = ctx;

    cf->cmd_type = NGX_HTTP_LOC_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;

}

static ngx_int_t

‌ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,

    ngx_str_t *regex, ngx_uint_t caseless)

{

#if (NGX_PCRE)

    ngx_regex_compile_t  rc;

    u_char               errstr[NGX_MAX_CONF_ERRSTR];

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pattern = *regex;

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

#if (NGX_HAVE_CASELESS_FILESYSTEM)

    rc.options = NGX_REGEX_CASELESS;

#else

    rc.options = caseless ? NGX_REGEX_CASELESS : 0;

#endif

    clcf->regex = ngx_http_regex_compile(cf, &rc);

    if (clcf->regex == NULL) {

        return NGX_ERROR;

    }

    clcf->name = *regex;

    return NGX_OK;

#else

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "using regex \"%V\" requires PCRE library",

                       regex);

    return NGX_ERROR;

#endif

}

static char *

‌ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    char        *rv;

    ngx_conf_t   save;

    if (clcf->types == NULL) {

        clcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t));

        if (clcf->types == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    save = *cf;

    cf->handler = ngx_http_core_type;

    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;

}

static char *

‌ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t       *value, *content_type, *old;

    ngx_uint_t       i, n, hash;

    ngx_hash_key_t  *type;

    value = cf->args->elts;

    if (ngx_strcmp(value[0].data, "include") == 0) {

        if (cf->args->nelts != 2) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid number of arguments"

                               " in \"include\" directive");

            return NGX_CONF_ERROR;

        }

        return ngx_conf_include(cf, dummy, conf);

    }

    content_type = ngx_palloc(cf->pool, sizeof(ngx_str_t));

    if (content_type == NULL) {

        return NGX_CONF_ERROR;

    }

    *content_type = value[0];

    for (i = 1; i < cf->args->nelts; i++) {

        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);

        type = clcf->types->elts;

        for (n = 0; n < clcf->types->nelts; n++) {

            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {

                old = type[n].value;

                type[n].value = content_type;

                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                                   "duplicate extension \"%V\", "

                                   "content type: \"%V\", "

                                   "previous content type: \"%V\"",

                                   &value[i], content_type, old);

                goto next;

            }

        }

        type = ngx_array_push(clcf->types);

        if (type == NULL) {

            return NGX_CONF_ERROR;

        }

        type->key = value[i];

        type->key_hash = hash;

        type->value = content_type;

    next:

        continue;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_core_preconfiguration(ngx_conf_t *cf)

{

    return ngx_http_variables_add_core_vars(cf);

}

static void *

‌ngx_http_core_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t));

    if (cmcf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&cmcf->servers, cf->pool, 4,

                       sizeof(ngx_http_core_srv_conf_t *))

        != NGX_OK)

    {

        return NULL;

    }

    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;

    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;

    cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;

    cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;

    return cmcf;

}

static char *

‌ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_core_main_conf_t *cmcf = conf;

    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);

    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,

                             ngx_cacheline_size);

    cmcf->server_names_hash_bucket_size =

            ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);

    ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);

    ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);

    cmcf->variables_hash_bucket_size =

               ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);

    if (cmcf->ncaptures) {

        cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;

    }

    return NGX_CONF_OK;

}

static void *

‌ngx_http_core_create_srv_conf(ngx_conf_t *cf)

{

    ngx_http_core_srv_conf_t  *cscf;

    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t));

    if (cscf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->client_large_buffers.num = 0;

     */

    if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,

                       sizeof(ngx_http_server_name_t))

        != NGX_OK)

    {

        return NULL;

    }

    cscf->connection_pool_size = NGX_CONF_UNSET_SIZE;

    cscf->request_pool_size = NGX_CONF_UNSET_SIZE;

    cscf->client_header_timeout = NGX_CONF_UNSET_MSEC;

    cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE;

    cscf->ignore_invalid_headers = NGX_CONF_UNSET;

    cscf->merge_slashes = NGX_CONF_UNSET;

    cscf->underscores_in_headers = NGX_CONF_UNSET;

    return cscf;

}

static char *

‌ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_core_srv_conf_t *prev = parent;

    ngx_http_core_srv_conf_t *conf = child;

    ngx_str_t                name;

    ngx_http_server_name_t  *sn;

    /* TODO: it does not merge, it inits only */

    ngx_conf_merge_size_value(conf->connection_pool_size,

                              prev->connection_pool_size, 256);

    ngx_conf_merge_size_value(conf->request_pool_size,

                              prev->request_pool_size, 4096);

    ngx_conf_merge_msec_value(conf->client_header_timeout,

                              prev->client_header_timeout, 60000);

    ngx_conf_merge_size_value(conf->client_header_buffer_size,

                              prev->client_header_buffer_size, 1024);

    ngx_conf_merge_bufs_value(conf->large_client_header_buffers,

                              prev->large_client_header_buffers,

                              4, 8192);

    if (conf->large_client_header_buffers.size < conf->connection_pool_size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the \"large_client_header_buffers\" size must be "

                           "equal to or greater than \"connection_pool_size\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->ignore_invalid_headers,

                              prev->ignore_invalid_headers, 1);

    ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1);

    ngx_conf_merge_value(conf->underscores_in_headers,

                              prev->underscores_in_headers, 0);

    if (conf->server_names.nelts == 0) {

        /* the array has 4 empty preallocated elements, so push cannot fail */

        sn = ngx_array_push(&conf->server_names);

#if (NGX_PCRE)

        sn->regex = NULL;

#endif

        sn->server = conf;

        ngx_str_set(&sn->name, "");

    }

    sn = conf->server_names.elts;

    name = sn[0].name;

#if (NGX_PCRE)

    if (sn->regex) {

        name.len++;

        name.data--;

    } else

#endif

    if (name.data[0] == '.') {

        name.len--;

        name.data++;

    }

    conf->server_name.len = name.len;

    conf->server_name.data = ngx_pstrdup(cf->pool, &name);

    if (conf->server_name.data == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static void *

‌ngx_http_core_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t));

    if (clcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     clcf->root = { 0, NULL };

     *     clcf->limit_except = 0;

     *     clcf->post_action = { 0, NULL };

     *     clcf->types = NULL;

     *     clcf->default_type = { 0, NULL };

     *     clcf->error_log = NULL;

     *     clcf->error_pages = NULL;

     *     clcf->try_files = NULL;

     *     clcf->client_body_path = NULL;

     *     clcf->regex = NULL;

     *     clcf->exact_match = 0;

     *     clcf->auto_redirect = 0;

     *     clcf->alias = 0;

     *     clcf->gzip_proxied = 0;

     *     clcf->keepalive_disable = 0;

     */

    clcf->client_max_body_size = NGX_CONF_UNSET;

    clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;

    clcf->client_body_timeout = NGX_CONF_UNSET_MSEC;

    clcf->satisfy = NGX_CONF_UNSET_UINT;

    clcf->if_modified_since = NGX_CONF_UNSET_UINT;

    clcf->max_ranges = NGX_CONF_UNSET_UINT;

    clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT;

    clcf->client_body_in_single_buffer = NGX_CONF_UNSET;

    clcf->internal = NGX_CONF_UNSET;

    clcf->sendfile = NGX_CONF_UNSET;

    clcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;

#if (NGX_HAVE_FILE_AIO)

    clcf->aio = NGX_CONF_UNSET;

#endif

    clcf->read_ahead = NGX_CONF_UNSET_SIZE;

    clcf->directio = NGX_CONF_UNSET;

    clcf->directio_alignment = NGX_CONF_UNSET;

    clcf->tcp_nopush = NGX_CONF_UNSET;

    clcf->tcp_nodelay = NGX_CONF_UNSET;

    clcf->send_timeout = NGX_CONF_UNSET_MSEC;

    clcf->send_lowat = NGX_CONF_UNSET_SIZE;

    clcf->postpone_output = NGX_CONF_UNSET_SIZE;

    clcf->limit_rate = NGX_CONF_UNSET_SIZE;

    clcf->limit_rate_after = NGX_CONF_UNSET_SIZE;

    clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;

    clcf->keepalive_header = NGX_CONF_UNSET;

    clcf->keepalive_requests = NGX_CONF_UNSET_UINT;

    clcf->lingering_close = NGX_CONF_UNSET_UINT;

    clcf->lingering_time = NGX_CONF_UNSET_MSEC;

    clcf->lingering_timeout = NGX_CONF_UNSET_MSEC;

    clcf->resolver_timeout = NGX_CONF_UNSET_MSEC;

    clcf->reset_timedout_connection = NGX_CONF_UNSET;

    clcf->server_name_in_redirect = NGX_CONF_UNSET;

    clcf->port_in_redirect = NGX_CONF_UNSET;

    clcf->msie_padding = NGX_CONF_UNSET;

    clcf->msie_refresh = NGX_CONF_UNSET;

    clcf->log_not_found = NGX_CONF_UNSET;

    clcf->log_subrequest = NGX_CONF_UNSET;

    clcf->recursive_error_pages = NGX_CONF_UNSET;

    clcf->server_tokens = NGX_CONF_UNSET;

    clcf->chunked_transfer_encoding = NGX_CONF_UNSET;

    clcf->etag = NGX_CONF_UNSET;

    clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;

    clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;

    clcf->open_file_cache = NGX_CONF_UNSET_PTR;

    clcf->open_file_cache_valid = NGX_CONF_UNSET;

    clcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT;

    clcf->open_file_cache_errors = NGX_CONF_UNSET;

    clcf->open_file_cache_events = NGX_CONF_UNSET;

#if (NGX_HTTP_GZIP)

    clcf->gzip_vary = NGX_CONF_UNSET;

    clcf->gzip_http_version = NGX_CONF_UNSET_UINT;

#if (NGX_PCRE)

    clcf->gzip_disable = NGX_CONF_UNSET_PTR;

#endif

    clcf->gzip_disable_msie6 = 3;

#if (NGX_HTTP_DEGRADATION)

    clcf->gzip_disable_degradation = 3;

#endif

#endif

#if (NGX_HAVE_OPENAT)

    clcf->disable_symlinks = NGX_CONF_UNSET_UINT;

    clcf->disable_symlinks_from = NGX_CONF_UNSET_PTR;

#endif

    return clcf;

}

‌static ngx_str_t  ngx_http_core_text_html_type = ngx_string("text/html");

‌static ngx_str_t  ngx_http_core_image_gif_type = ngx_string("image/gif");

‌static ngx_str_t  ngx_http_core_image_jpeg_type = ngx_string("image/jpeg");

‌static ngx_hash_key_t  ngx_http_core_default_types[] = {

    { ngx_string("html"), 0, &ngx_http_core_text_html_type },

    { ngx_string("gif"), 0, &ngx_http_core_image_gif_type },

    { ngx_string("jpg"), 0, &ngx_http_core_image_jpeg_type },

    { ngx_null_string, 0, NULL }

};

static char *

‌ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_core_loc_conf_t *prev = parent;

    ngx_http_core_loc_conf_t *conf = child;

    ngx_uint_t        i;

    ngx_hash_key_t   *type;

    ngx_hash_init_t   types_hash;

    if (conf->root.data == NULL) {

        conf->alias = prev->alias;

        conf->root = prev->root;

        conf->root_lengths = prev->root_lengths;

        conf->root_values = prev->root_values;

        if (prev->root.data == NULL) {

            ngx_str_set(&conf->root, "html");

            if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    }

    if (conf->post_action.data == NULL) {

        conf->post_action = prev->post_action;

    }

    ngx_conf_merge_uint_value(conf->types_hash_max_size,

                              prev->types_hash_max_size, 1024);

    ngx_conf_merge_uint_value(conf->types_hash_bucket_size,

                              prev->types_hash_bucket_size, 64);

    conf->types_hash_bucket_size = ngx_align(conf->types_hash_bucket_size,

                                             ngx_cacheline_size);

    /*

     * the special handling of the "types" directive in the "http" section

     * to inherit the http's conf->types_hash to all servers

     */

    if (prev->types && prev->types_hash.buckets == NULL) {

        types_hash.hash = &prev->types_hash;

        types_hash.key = ngx_hash_key_lc;

        types_hash.max_size = conf->types_hash_max_size;

        types_hash.bucket_size = conf->types_hash_bucket_size;

        types_hash.name = "types_hash";

        types_hash.pool = cf->pool;

        types_hash.temp_pool = NULL;

        if (ngx_hash_init(&types_hash, prev->types->elts, prev->types->nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    if (conf->types == NULL) {

        conf->types = prev->types;

        conf->types_hash = prev->types_hash;

    }

    if (conf->types == NULL) {

        conf->types = ngx_array_create(cf->pool, 3, sizeof(ngx_hash_key_t));

        if (conf->types == NULL) {

            return NGX_CONF_ERROR;

        }

        for (i = 0; ngx_http_core_default_types[i].key.len; i++) {

            type = ngx_array_push(conf->types);

            if (type == NULL) {

                return NGX_CONF_ERROR;

            }

            type->key = ngx_http_core_default_types[i].key;

            type->key_hash =

                       ngx_hash_key_lc(ngx_http_core_default_types[i].key.data,

                                       ngx_http_core_default_types[i].key.len);

            type->value = ngx_http_core_default_types[i].value;

        }

    }

    if (conf->types_hash.buckets == NULL) {

        types_hash.hash = &conf->types_hash;

        types_hash.key = ngx_hash_key_lc;

        types_hash.max_size = conf->types_hash_max_size;

        types_hash.bucket_size = conf->types_hash_bucket_size;

        types_hash.name = "types_hash";

        types_hash.pool = cf->pool;

        types_hash.temp_pool = NULL;

        if (ngx_hash_init(&types_hash, conf->types->elts, conf->types->nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    if (conf->error_log == NULL) {

        if (prev->error_log) {

            conf->error_log = prev->error_log;

        } else {

            conf->error_log = &cf->cycle->new_log;

        }

    }

    if (conf->error_pages == NULL && prev->error_pages) {

        conf->error_pages = prev->error_pages;

    }

    ngx_conf_merge_str_value(conf->default_type,

                              prev->default_type, "text/plain");

    ngx_conf_merge_off_value(conf->client_max_body_size,

                              prev->client_max_body_size, 1 * 1024 * 1024);

    ngx_conf_merge_size_value(conf->client_body_buffer_size,

                              prev->client_body_buffer_size,

                              (size_t) 2 * ngx_pagesize);

    ngx_conf_merge_msec_value(conf->client_body_timeout,

                              prev->client_body_timeout, 60000);

    ngx_conf_merge_bitmask_value(conf->keepalive_disable,

                              prev->keepalive_disable,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6));

    ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,

                              NGX_HTTP_SATISFY_ALL);

    ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,

                              NGX_HTTP_IMS_EXACT);

    ngx_conf_merge_uint_value(conf->max_ranges, prev->max_ranges,

                              NGX_MAX_INT32_VALUE);

    ngx_conf_merge_uint_value(conf->client_body_in_file_only,

                              prev->client_body_in_file_only,

                              NGX_HTTP_REQUEST_BODY_FILE_OFF);

    ngx_conf_merge_value(conf->client_body_in_single_buffer,

                              prev->client_body_in_single_buffer, 0);

    ngx_conf_merge_value(conf->internal, prev->internal, 0);

    ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);

    ngx_conf_merge_size_value(conf->sendfile_max_chunk,

                              prev->sendfile_max_chunk, 0);

#if (NGX_HAVE_FILE_AIO)

    ngx_conf_merge_value(conf->aio, prev->aio, NGX_HTTP_AIO_OFF);

#endif

    ngx_conf_merge_size_value(conf->read_ahead, prev->read_ahead, 0);

    ngx_conf_merge_off_value(conf->directio, prev->directio,

                              NGX_OPEN_FILE_DIRECTIO_OFF);

    ngx_conf_merge_off_value(conf->directio_alignment, prev->directio_alignment,

                              512);

    ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);

    ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);

    ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000);

    ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);

    ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output,

                              1460);

    ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0);

    ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after,

                              0);

    ngx_conf_merge_msec_value(conf->keepalive_timeout,

                              prev->keepalive_timeout, 75000);

    ngx_conf_merge_sec_value(conf->keepalive_header,

                              prev->keepalive_header, 0);

    ngx_conf_merge_uint_value(conf->keepalive_requests,

                              prev->keepalive_requests, 100);

    ngx_conf_merge_uint_value(conf->lingering_close,

                              prev->lingering_close, NGX_HTTP_LINGERING_ON);

    ngx_conf_merge_msec_value(conf->lingering_time,

                              prev->lingering_time, 30000);

    ngx_conf_merge_msec_value(conf->lingering_timeout,

                              prev->lingering_timeout, 5000);

    ngx_conf_merge_msec_value(conf->resolver_timeout,

                              prev->resolver_timeout, 30000);

    if (conf->resolver == NULL) {

        if (prev->resolver == NULL) {

            /*

             * create dummy resolver in http {} context

             * to inherit it in all servers

             */

            prev->resolver = ngx_resolver_create(cf, NULL, 0);

            if (prev->resolver == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        conf->resolver = prev->resolver;

    }

    if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,

                              prev->client_body_temp_path,

                              &ngx_http_client_temp_path)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->reset_timedout_connection,

                              prev->reset_timedout_connection, 0);

    ngx_conf_merge_value(conf->server_name_in_redirect,

                              prev->server_name_in_redirect, 0);

    ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);

    ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);

    ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);

    ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);

    ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0);

    ngx_conf_merge_value(conf->recursive_error_pages,

                              prev->recursive_error_pages, 0);

    ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1);

    ngx_conf_merge_value(conf->chunked_transfer_encoding,

                              prev->chunked_transfer_encoding, 1);

    ngx_conf_merge_value(conf->etag, prev->etag, 1);

    ngx_conf_merge_ptr_value(conf->open_file_cache,

                              prev->open_file_cache, NULL);

    ngx_conf_merge_sec_value(conf->open_file_cache_valid,

                              prev->open_file_cache_valid, 60);

    ngx_conf_merge_uint_value(conf->open_file_cache_min_uses,

                              prev->open_file_cache_min_uses, 1);

    ngx_conf_merge_sec_value(conf->open_file_cache_errors,

                              prev->open_file_cache_errors, 0);

    ngx_conf_merge_sec_value(conf->open_file_cache_events,

                              prev->open_file_cache_events, 0);

#if (NGX_HTTP_GZIP)

    ngx_conf_merge_value(conf->gzip_vary, prev->gzip_vary, 0);

    ngx_conf_merge_uint_value(conf->gzip_http_version, prev->gzip_http_version,

                              NGX_HTTP_VERSION_11);

    ngx_conf_merge_bitmask_value(conf->gzip_proxied, prev->gzip_proxied,

                              (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));

#if (NGX_PCRE)

    ngx_conf_merge_ptr_value(conf->gzip_disable, prev->gzip_disable, NULL);

#endif

    if (conf->gzip_disable_msie6 == 3) {

        conf->gzip_disable_msie6 =

            (prev->gzip_disable_msie6 == 3) ? 0 : prev->gzip_disable_msie6;

    }

#if (NGX_HTTP_DEGRADATION)

    if (conf->gzip_disable_degradation == 3) {

        conf->gzip_disable_degradation =

            (prev->gzip_disable_degradation == 3) ?

                 0 : prev->gzip_disable_degradation;

    }

#endif

#endif

#if (NGX_HAVE_OPENAT)

    ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks,

                              NGX_DISABLE_SYMLINKS_OFF);

    ngx_conf_merge_ptr_value(conf->disable_symlinks_from,

                             prev->disable_symlinks_from, NULL);

#endif

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_srv_conf_t *cscf = conf;

    ngx_str_t              *value, size;

    ngx_url_t               u;

    ngx_uint_t              n;

    ngx_http_listen_opt_t   lsopt;

    cscf->listen = 1;

    value = cf->args->elts;

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.listen = 1;

    u.default_port = 80;

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "%s in \"%V\" of the \"listen\" directive",

                               u.err, &u.url);

        }

        return NGX_CONF_ERROR;

    }

    ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

    ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen);

    lsopt.socklen = u.socklen;

    lsopt.backlog = NGX_LISTEN_BACKLOG;

    lsopt.rcvbuf = -1;

    lsopt.sndbuf = -1;

#if (NGX_HAVE_SETFIB)

    lsopt.setfib = -1;

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

    lsopt.fastopen = -1;

#endif

    lsopt.wildcard = u.wildcard;

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    lsopt.ipv6only = 1;

#endif

    (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr,

                         NGX_SOCKADDR_STRLEN, 1);

    for (n = 2; n < cf->args->nelts; n++) {

        if (ngx_strcmp(value[n].data, "default_server") == 0

            || ngx_strcmp(value[n].data, "default") == 0)

        {

            lsopt.default_server = 1;

            continue;

        }

        if (ngx_strcmp(value[n].data, "bind") == 0) {

            lsopt.set = 1;

            lsopt.bind = 1;

            continue;

        }

#if (NGX_HAVE_SETFIB)

        if (ngx_strncmp(value[n].data, "setfib=", 7) == 0) {

            lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7);

            lsopt.set = 1;

            lsopt.bind = 1;

            if (lsopt.setfib == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid setfib \"%V\"", &value[n]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

#endif

#if (NGX_HAVE_TCP_FASTOPEN)

        if (ngx_strncmp(value[n].data, "fastopen=", 9) == 0) {

            lsopt.fastopen = ngx_atoi(value[n].data + 9, value[n].len - 9);

            lsopt.set = 1;

            lsopt.bind = 1;

            if (lsopt.fastopen == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid fastopen \"%V\"", &value[n]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

#endif

        if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) {

            lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);

            lsopt.set = 1;

            lsopt.bind = 1;

            if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid backlog \"%V\"", &value[n]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[n].data, "rcvbuf=", 7) == 0) {

            size.len = value[n].len - 7;

            size.data = value[n].data + 7;

            lsopt.rcvbuf = ngx_parse_size(&size);

            lsopt.set = 1;

            lsopt.bind = 1;

            if (lsopt.rcvbuf == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid rcvbuf \"%V\"", &value[n]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[n].data, "sndbuf=", 7) == 0) {

            size.len = value[n].len - 7;

            size.data = value[n].data + 7;

            lsopt.sndbuf = ngx_parse_size(&size);

            lsopt.set = 1;

            lsopt.bind = 1;

            if (lsopt.sndbuf == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid sndbuf \"%V\"", &value[n]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) {

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)

            lsopt.accept_filter = (char *) &value[n].data[14];

            lsopt.set = 1;

            lsopt.bind = 1;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "accept filters \"%V\" are not supported "

                               "on this platform, ignored",

                               &value[n]);

#endif

            continue;

        }

        if (ngx_strcmp(value[n].data, "deferred") == 0) {

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)

            lsopt.deferred_accept = 1;

            lsopt.set = 1;

            lsopt.bind = 1;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "the deferred accept is not supported "

                               "on this platform, ignored");

#endif

            continue;

        }

        if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) {

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

            struct sockaddr  *sa;

            sa = &lsopt.u.sockaddr;

            if (sa->sa_family == AF_INET6) {

                if (ngx_strcmp(&value[n].data[10], "n") == 0) {

                    lsopt.ipv6only = 1;

                } else if (ngx_strcmp(&value[n].data[10], "ff") == 0) {

                    lsopt.ipv6only = 0;

                } else {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "invalid ipv6only flags \"%s\"",

                                       &value[n].data[9]);

                    return NGX_CONF_ERROR;

                }

                lsopt.set = 1;

                lsopt.bind = 1;

            } else {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "ipv6only is not supported "

                                   "on addr \"%s\", ignored", lsopt.addr);

            }

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "ipv6only is not supported "

                               "on this platform");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strcmp(value[n].data, "ssl") == 0) {

#if (NGX_HTTP_SSL)

            lsopt.ssl = 1;

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "the \"ssl\" parameter requires "

                               "ngx_http_ssl_module");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strcmp(value[n].data, "spdy") == 0) {

#if (NGX_HTTP_SPDY)

            lsopt.spdy = 1;

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "the \"spdy\" parameter requires "

                               "ngx_http_spdy_module");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {

            if (ngx_strcmp(&value[n].data[13], "on") == 0) {

                lsopt.so_keepalive = 1;

            } else if (ngx_strcmp(&value[n].data[13], "off") == 0) {

                lsopt.so_keepalive = 2;

            } else {

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

                u_char     *p, *end;

                ngx_str_t   s;

                end = value[n].data + value[n].len;

                s.data = value[n].data + 13;

                p = ngx_strlchr(s.data, end, ':');

                if (p == NULL) {

                    p = end;

                }

                if (p > s.data) {

                    s.len = p - s.data;

                    lsopt.tcp_keepidle = ngx_parse_time(&s, 1);

                    if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                s.data = (p < end) ? (p + 1) : end;

                p = ngx_strlchr(s.data, end, ':');

                if (p == NULL) {

                    p = end;

                }

                if (p > s.data) {

                    s.len = p - s.data;

                    lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);

                    if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                s.data = (p < end) ? (p + 1) : end;

                if (s.data < end) {

                    s.len = end - s.data;

                    lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);

                    if (lsopt.tcp_keepcnt == NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0

                    && lsopt.tcp_keepcnt == 0)

                {

                    goto invalid_so_keepalive;

                }

                lsopt.so_keepalive = 1;

#else

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "the \"so_keepalive\" parameter accepts "

                                   "only \"on\" or \"off\" on this platform");

                return NGX_CONF_ERROR;

#endif

            }

            lsopt.set = 1;

            lsopt.bind = 1;

            continue;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

        invalid_so_keepalive:

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid so_keepalive value: \"%s\"",

                               &value[n].data[13]);

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) {

            lsopt.proxy_protocol = 1;

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[n]);

        return NGX_CONF_ERROR;

    }

    if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {

        return NGX_CONF_OK;

    }

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_srv_conf_t *cscf = conf;

    u_char                   ch;

    ngx_str_t               *value;

    ngx_uint_t               i;

    ngx_http_server_name_t  *sn;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        ch = value[i].data[0];

        if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))

            || (ch == '.' && value[i].len < 2))

        {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "server name \"%V\" is invalid", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (ngx_strchr(value[i].data, '/')) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "server name \"%V\" has suspicious symbols",

                               &value[i]);

        }

        sn = ngx_array_push(&cscf->server_names);

        if (sn == NULL) {

            return NGX_CONF_ERROR;

        }

#if (NGX_PCRE)

        sn->regex = NULL;

#endif

        sn->server = cscf;

        if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {

            sn->name = cf->cycle->hostname;

        } else {

            sn->name = value[i];

        }

        if (value[i].data[0] != '~') {

            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);

            continue;

        }

#if (NGX_PCRE)

        {

        u_char               *p;

        ngx_regex_compile_t   rc;

        u_char                errstr[NGX_MAX_CONF_ERRSTR];

        if (value[i].len == 1) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "empty regex in server name \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        value[i].len--;

        value[i].data++;

        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

        rc.pattern = value[i];

        rc.err.len = NGX_MAX_CONF_ERRSTR;

        rc.err.data = errstr;

        for (p = value[i].data; p < value[i].data + value[i].len; p++) {

            if (*p >= 'A' && *p <= 'Z') {

                rc.options = NGX_REGEX_CASELESS;

                break;

            }

        }

        sn->regex = ngx_http_regex_compile(cf, &rc);

        if (sn->regex == NULL) {

            return NGX_CONF_ERROR;

        }

        sn->name = value[i];

        cscf->captures = (rc.captures > 0);

        }

#else

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "using regex \"%V\" "

                           "requires PCRE library", &value[i]);

        return NGX_CONF_ERROR;

#endif

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t                  *value;

    ngx_int_t                   alias;

    ngx_uint_t                  n;

    ngx_http_script_compile_t   sc;

    alias = (cmd->name.len == sizeof("alias") - 1) ? 1 : 0;

    if (clcf->root.data) {

        if ((clcf->alias != 0) == alias) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"%V\" directive is duplicate",

                               &cmd->name);

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"%V\" directive is duplicate, "

                               "\"%s\" directive was specified earlier",

                               &cmd->name, clcf->alias ? "alias" : "root");

        }

        return NGX_CONF_ERROR;

    }

    if (clcf->named && alias) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the \"alias\" directive cannot be used "

                           "inside the named location");

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    if (ngx_strstr(value[1].data, "$document_root")

        || ngx_strstr(value[1].data, "${document_root}"))

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the $document_root variable cannot be used "

                           "in the \"%V\" directive",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    if (ngx_strstr(value[1].data, "$realpath_root")

        || ngx_strstr(value[1].data, "${realpath_root}"))

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the $realpath_root variable cannot be used "

                           "in the \"%V\" directive",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    clcf->alias = alias ? clcf->name.len : 0;

    clcf->root = value[1];

    if (!alias && clcf->root.data[clcf->root.len - 1] == '/') {

        clcf->root.len--;

    }

    if (clcf->root.data[0] != '$') {

        if (ngx_conf_full_name(cf->cycle, &clcf->root, 0) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    n = ngx_http_script_variables_count(&clcf->root);

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.variables = n;

#if (NGX_PCRE)

    if (alias && clcf->regex) {

        clcf->alias = NGX_MAX_SIZE_T_VALUE;

        n = 1;

    }

#endif

    if (n) {

        sc.cf = cf;

        sc.source = &clcf->root;

        sc.lengths = &clcf->root_lengths;

        sc.values = &clcf->root_values;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

‌static ngx_http_method_name_t  ngx_methods_names[] = {

   { (u_char *) "GET",       (uint32_t) ~NGX_HTTP_GET },

   { (u_char *) "HEAD",      (uint32_t) ~NGX_HTTP_HEAD },

   { (u_char *) "POST",      (uint32_t) ~NGX_HTTP_POST },

   { (u_char *) "PUT",       (uint32_t) ~NGX_HTTP_PUT },

   { (u_char *) "DELETE",    (uint32_t) ~NGX_HTTP_DELETE },

   { (u_char *) "MKCOL",     (uint32_t) ~NGX_HTTP_MKCOL },

   { (u_char *) "COPY",      (uint32_t) ~NGX_HTTP_COPY },

   { (u_char *) "MOVE",      (uint32_t) ~NGX_HTTP_MOVE },

   { (u_char *) "OPTIONS",   (uint32_t) ~NGX_HTTP_OPTIONS },

   { (u_char *) "PROPFIND",  (uint32_t) ~NGX_HTTP_PROPFIND },

   { (u_char *) "PROPPATCH", (uint32_t) ~NGX_HTTP_PROPPATCH },

   { (u_char *) "LOCK",      (uint32_t) ~NGX_HTTP_LOCK },

   { (u_char *) "UNLOCK",    (uint32_t) ~NGX_HTTP_UNLOCK },

   { (u_char *) "PATCH",     (uint32_t) ~NGX_HTTP_PATCH },

   { NULL, 0 }

};

static char *

‌ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *pclcf = conf;

    char                      *rv;

    void                      *mconf;

    ngx_str_t                 *value;

    ngx_uint_t                 i;

    ngx_conf_t                 save;

    ngx_http_module_t         *module;

    ngx_http_conf_ctx_t       *ctx, *pctx;

    ngx_http_method_name_t    *name;

    ngx_http_core_loc_conf_t  *clcf;

    if (pclcf->limit_except) {

        return "duplicate";

    }

    pclcf->limit_except = 0xffffffff;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        for (name = ngx_methods_names; name->name; name++) {

            if (ngx_strcasecmp(value[i].data, name->name) == 0) {

                pclcf->limit_except &= name->method;

                goto next;

            }

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid method \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    next:

        continue;

    }

    if (!(pclcf->limit_except & NGX_HTTP_GET)) {

        pclcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD;

    }

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    pctx = cf->ctx;

    ctx->main_conf = pctx->main_conf;

    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->create_loc_conf) {

            mconf = module->create_loc_conf(cf);

            if (mconf == NULL) {

                 return NGX_CONF_ERROR;

            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;

        }

    }

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

    pclcf->limit_except_loc_conf = ctx->loc_conf;

    clcf->loc_conf = ctx->loc_conf;

    clcf->name = pclcf->name;

    clcf->noname = 1;

    clcf->lmt_excpt = 1;

    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    save = *cf;

    cf->ctx = ctx;

    cf->cmd_type = NGX_HTTP_LMT_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;

}

static char *

‌ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t  *value;

    if (clcf->directio != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF;

        return NGX_CONF_OK;

    }

    clcf->directio = ngx_parse_offset(&value[1]);

    if (clcf->directio == (off_t) NGX_ERROR) {

        return "invalid value";

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    u_char                            *p;

    ngx_int_t                          overwrite;

    ngx_str_t                         *value, uri, args;

    ngx_uint_t                         i, n;

    ngx_http_err_page_t               *err;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    if (clcf->error_pages == NULL) {

        clcf->error_pages = ngx_array_create(cf->pool, 4,

                                             sizeof(ngx_http_err_page_t));

        if (clcf->error_pages == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    i = cf->args->nelts - 2;

    if (value[i].data[0] == '=') {

        if (i == 1) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (value[i].len > 1) {

            overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1);

            if (overwrite == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

        } else {

            overwrite = 0;

        }

        n = 2;

    } else {

        overwrite = -1;

        n = 1;

    }

    uri = value[cf->args->nelts - 1];

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &uri;

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    ngx_str_null(&args);

    if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {

        p = (u_char *) ngx_strchr(uri.data, '?');

        if (p) {

            cv.value.len = p - uri.data;

            cv.value.data = uri.data;

            p++;

            args.len = (uri.data + uri.len) - p;

            args.data = p;

        }

    }

    for (i = 1; i < cf->args->nelts - n; i++) {

        err = ngx_array_push(clcf->error_pages);

        if (err == NULL) {

            return NGX_CONF_ERROR;

        }

        err->status = ngx_atoi(value[i].data, value[i].len);

        if (err->status == NGX_ERROR || err->status == 499) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (err->status < 300 || err->status > 599) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "value \"%V\" must be between 300 and 599",

                               &value[i]);

            return NGX_CONF_ERROR;

        }

        err->overwrite = overwrite;

        if (overwrite == -1) {

            switch (err->status) {

                case NGX_HTTP_TO_HTTPS:

                case NGX_HTTPS_CERT_ERROR:

                case NGX_HTTPS_NO_CERT:

                    err->overwrite = NGX_HTTP_BAD_REQUEST;

                default:

                    break;

            }

        }

        err->value = cv;

        err->args = args;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t                  *value;

    ngx_int_t                   code;

    ngx_uint_t                  i, n;

    ngx_http_try_file_t        *tf;

    ngx_http_script_compile_t   sc;

    ngx_http_core_main_conf_t  *cmcf;

    if (clcf->try_files) {

        return "is duplicate";

    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    cmcf->try_files = 1;

    tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t));

    if (tf == NULL) {

        return NGX_CONF_ERROR;

    }

    clcf->try_files = tf;

    value = cf->args->elts;

    for (i = 0; i < cf->args->nelts - 1; i++) {

        tf[i].name = value[i + 1];

        if (tf[i].name.len > 0

            && tf[i].name.data[tf[i].name.len - 1] == '/'

            && i + 2 < cf->args->nelts)

        {

            tf[i].test_dir = 1;

            tf[i].name.len--;

            tf[i].name.data[tf[i].name.len] = '\0';

        }

        n = ngx_http_script_variables_count(&tf[i].name);

        if (n) {

            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

            sc.cf = cf;

            sc.source = &tf[i].name;

            sc.lengths = &tf[i].lengths;

            sc.values = &tf[i].values;

            sc.variables = n;

            sc.complete_lengths = 1;

            sc.complete_values = 1;

            if (ngx_http_script_compile(&sc) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        } else {

            /* add trailing '\0' to length */

            tf[i].name.len++;

        }

    }

    if (tf[i - 1].name.data[0] == '=') {

        code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2);

        if (code == NGX_ERROR || code > 999) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid code \"%*s\"",

                               tf[i - 1].name.len - 1, tf[i - 1].name.data);

            return NGX_CONF_ERROR;

        }

        tf[i].code = code;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    time_t       inactive;

    ngx_str_t   *value, s;

    ngx_int_t    max;

    ngx_uint_t   i;

    if (clcf->open_file_cache != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    max = 0;

    inactive = 60;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {

            max = ngx_atoi(value[i].data + 4, value[i].len - 4);

            if (max <= 0) {

                goto failed;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {

            s.len = value[i].len - 9;

            s.data = value[i].data + 9;

            inactive = ngx_parse_time(&s, 1);

            if (inactive == (time_t) NGX_ERROR) {

                goto failed;

            }

            continue;

        }

        if (ngx_strcmp(value[i].data, "off") == 0) {

            clcf->open_file_cache = NULL;

            continue;

        }

    failed:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid \"open_file_cache\" parameter \"%V\"",

                           &value[i]);

        return NGX_CONF_ERROR;

    }

    if (clcf->open_file_cache == NULL) {

        return NGX_CONF_OK;

    }

    if (max == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "\"open_file_cache\" must have the \"max\" parameter");

        return NGX_CONF_ERROR;

    }

    clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);

    if (clcf->open_file_cache) {

        return NGX_CONF_OK;

    }

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    return ngx_log_set_log(cf, &clcf->error_log);

}

static char *

‌ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t  *value;

    if (clcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {

        return "is duplicate";

    }

    value = cf->args->elts;

    clcf->keepalive_timeout = ngx_parse_time(&value[1], 0);

    if (clcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) {

        return "invalid value";

    }

    if (cf->args->nelts == 2) {

        return NGX_CONF_OK;

    }

    clcf->keepalive_header = ngx_parse_time(&value[2], 1);

    if (clcf->keepalive_header == (time_t) NGX_ERROR) {

        return "invalid value";

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    if (clcf->internal != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    clcf->internal = 1;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf = conf;

    ngx_str_t  *value;

    if (clcf->resolver) {

        return "is duplicate";

    }

    value = cf->args->elts;

    clcf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);

    if (clcf->resolver == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#if (NGX_HTTP_GZIP)

static char *

‌ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf = conf;

#if (NGX_PCRE)

    ngx_str_t            *value;

    ngx_uint_t            i;

    ngx_regex_elt_t      *re;

    ngx_regex_compile_t   rc;

    u_char                errstr[NGX_MAX_CONF_ERRSTR];

    if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) {

        clcf->gzip_disable = ngx_array_create(cf->pool, 2,

                                              sizeof(ngx_regex_elt_t));

        if (clcf->gzip_disable == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pool = cf->pool;

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "msie6") == 0) {

            clcf->gzip_disable_msie6 = 1;

            continue;

        }

#if (NGX_HTTP_DEGRADATION)

        if (ngx_strcmp(value[i].data, "degradation") == 0) {

            clcf->gzip_disable_degradation = 1;

            continue;

        }

#endif

        re = ngx_array_push(clcf->gzip_disable);

        if (re == NULL) {

            return NGX_CONF_ERROR;

        }

        rc.pattern = value[i];

        rc.options = NGX_REGEX_CASELESS;

        if (ngx_regex_compile(&rc) != NGX_OK) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);

            return NGX_CONF_ERROR;

        }

        re->regex = rc.regex;

        re->name = value[i].data;

    }

    return NGX_CONF_OK;

#else

    ngx_str_t   *value;

    ngx_uint_t   i;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "msie6") == 0) {

            clcf->gzip_disable_msie6 = 1;

            continue;

        }

#if (NGX_HTTP_DEGRADATION)

        if (ngx_strcmp(value[i].data, "degradation") == 0) {

            clcf->gzip_disable_degradation = 1;

            continue;

        }

#endif

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "without PCRE library \"gzip_disable\" supports "

                           "builtin \"msie6\" and \"degradation\" mask only");

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

#endif

}

#endif

#if (NGX_HAVE_OPENAT)

static char *

‌ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t *clcf = conf;

    ngx_str_t                         *value;

    ngx_uint_t                         i;

    ngx_http_compile_complex_value_t   ccv;

    if (clcf->disable_symlinks != NGX_CONF_UNSET_UINT) {

        return "is duplicate";

    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "off") == 0) {

            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;

            continue;

        }

        if (ngx_strcmp(value[i].data, "if_not_owner") == 0) {

            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_NOTOWNER;

            continue;

        }

        if (ngx_strcmp(value[i].data, "on") == 0) {

            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_ON;

            continue;

        }

        if (ngx_strncmp(value[i].data, "from=", 5) == 0) {

            value[i].len -= 5;

            value[i].data += 5;

            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

            ccv.cf = cf;

            ccv.value = &value[i];

            ccv.complex_value = ngx_palloc(cf->pool,

                                           sizeof(ngx_http_complex_value_t));

            if (ccv.complex_value == NULL) {

                return NGX_CONF_ERROR;

            }

            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

            clcf->disable_symlinks_from = ccv.complex_value;

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (clcf->disable_symlinks == NGX_CONF_UNSET_UINT) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%V\" must have \"off\", \"on\" "

                           "or \"if_not_owner\" parameter",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    if (cf->args->nelts == 2) {

        clcf->disable_symlinks_from = NULL;

        return NGX_CONF_OK;

    }

    if (clcf->disable_symlinks_from == NGX_CONF_UNSET_PTR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "duplicate parameters \"%V %V\"",

                           &value[1], &value[2]);

        return NGX_CONF_ERROR;

    }

    if (clcf->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"from=\" cannot be used with \"off\" parameter");

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

static char *

‌ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data)

{

#if (NGX_FREEBSD)

    ssize_t *np = data;

    if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"send_lowat\" must be less than %d "

                           "(sysctl net.inet.tcp.sendspace)",

                           ngx_freebsd_net_inet_tcp_sendspace);

        return NGX_CONF_ERROR;

    }

#elif !(NGX_HAVE_SO_SNDLOWAT)

    ssize_t *np = data;

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"send_lowat\" is not supported, ignored");

    *np = 0;

#endif

    return NGX_CONF_OK;

}

static char *

‌ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)

{

    size_t *sp = data;

    if (*sp < NGX_MIN_POOL_SIZE) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the pool size must be no less than %uz",

                           NGX_MIN_POOL_SIZE);

        return NGX_CONF_ERROR;

    }

    if (*sp % NGX_POOL_ALIGNMENT) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the pool size must be a multiple of %uz",

                           NGX_POOL_ALIGNMENT);

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/ngx_http_request.c - nginx-1.7.9

Global variables defined

ngx_http_client_errors

ngx_http_headers_in

Functions defined

ngx_http_alloc_large_header_buffer

ngx_http_block_reading

ngx_http_close_connection

ngx_http_close_request

ngx_http_create_request

ngx_http_empty_handler

ngx_http_finalize_connection

ngx_http_finalize_request

ngx_http_find_virtual_server

ngx_http_free_request

ngx_http_init_connection

ngx_http_keepalive_handler

ngx_http_lingering_close_handler

ngx_http_log_error

ngx_http_log_error_handler

ngx_http_log_request

ngx_http_post_action

ngx_http_post_request

ngx_http_process_connection

ngx_http_process_header_line

ngx_http_process_host

ngx_http_process_multi_header_lines

ngx_http_process_request

ngx_http_process_request_header

ngx_http_process_request_headers

ngx_http_process_request_line

ngx_http_process_request_uri

ngx_http_process_unique_header_line

ngx_http_process_user_agent

ngx_http_read_request_header

ngx_http_request_empty_handler

ngx_http_request_finalizer

ngx_http_request_handler

ngx_http_run_posted_requests

ngx_http_send_special

ngx_http_set_keepalive

ngx_http_set_lingering_close

ngx_http_set_virtual_server

ngx_http_set_write_handler

ngx_http_ssl_handshake

ngx_http_ssl_handshake_handler

ngx_http_ssl_servername

ngx_http_terminate_handler

ngx_http_terminate_request

ngx_http_test_reading

ngx_http_validate_host

ngx_http_wait_request_handler

ngx_http_writer

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static void ngx_http_wait_request_handler(ngx_event_t *ev);

static void ngx_http_process_request_line(ngx_event_t *rev);

static void ngx_http_process_request_headers(ngx_event_t *rev);

static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,

    ngx_uint_t request_line);

static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,

    ngx_uint_t alloc);

static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,

    ngx_str_t *host);

static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,

    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,

    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);

static void ngx_http_request_handler(ngx_event_t *ev);

static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);

static void ngx_http_terminate_handler(ngx_http_request_t *r);

static void ngx_http_finalize_connection(ngx_http_request_t *r);

static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);

static void ngx_http_writer(ngx_http_request_t *r);

static void ngx_http_request_finalizer(ngx_http_request_t *r);

static void ngx_http_set_keepalive(ngx_http_request_t *r);

static void ngx_http_keepalive_handler(ngx_event_t *ev);

static void ngx_http_set_lingering_close(ngx_http_request_t *r);

static void ngx_http_lingering_close_handler(ngx_event_t *ev);

static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);

static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);

static void ngx_http_log_request(ngx_http_request_t *r);

static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);

static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,

    ngx_http_request_t *sr, u_char *buf, size_t len);

#if (NGX_HTTP_SSL)

static void ngx_http_ssl_handshake(ngx_event_t *rev);

static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);

#endif

‌static char *ngx_http_client_errors[] = {

    /* NGX_HTTP_PARSE_INVALID_METHOD */

    "client sent invalid method",

    /* NGX_HTTP_PARSE_INVALID_REQUEST */

    "client sent invalid request",

    /* NGX_HTTP_PARSE_INVALID_09_METHOD */

    "client sent invalid method in HTTP/0.9 request"

};

‌ngx_http_header_t  ngx_http_headers_in[] = {

    { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),

                 ngx_http_process_host },

    { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),

                 ngx_http_process_connection },

    { ngx_string("If-Modified-Since"),

                 offsetof(ngx_http_headers_in_t, if_modified_since),

                 ngx_http_process_unique_header_line },

    { ngx_string("If-Unmodified-Since"),

                 offsetof(ngx_http_headers_in_t, if_unmodified_since),

                 ngx_http_process_unique_header_line },

    { ngx_string("If-Match"),

                 offsetof(ngx_http_headers_in_t, if_match),

                 ngx_http_process_unique_header_line },

    { ngx_string("If-None-Match"),

                 offsetof(ngx_http_headers_in_t, if_none_match),

                 ngx_http_process_unique_header_line },

    { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),

                 ngx_http_process_user_agent },

    { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),

                 ngx_http_process_header_line },

    { ngx_string("Content-Length"),

                 offsetof(ngx_http_headers_in_t, content_length),

                 ngx_http_process_unique_header_line },

    { ngx_string("Content-Type"),

                 offsetof(ngx_http_headers_in_t, content_type),

                 ngx_http_process_header_line },

    { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),

                 ngx_http_process_header_line },

    { ngx_string("If-Range"),

                 offsetof(ngx_http_headers_in_t, if_range),

                 ngx_http_process_unique_header_line },

    { ngx_string("Transfer-Encoding"),

                 offsetof(ngx_http_headers_in_t, transfer_encoding),

                 ngx_http_process_header_line },

    { ngx_string("Expect"),

                 offsetof(ngx_http_headers_in_t, expect),

                 ngx_http_process_unique_header_line },

    { ngx_string("Upgrade"),

                 offsetof(ngx_http_headers_in_t, upgrade),

                 ngx_http_process_header_line },

#if (NGX_HTTP_GZIP)

    { ngx_string("Accept-Encoding"),

                 offsetof(ngx_http_headers_in_t, accept_encoding),

                 ngx_http_process_header_line },

    { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),

                 ngx_http_process_header_line },

#endif

    { ngx_string("Authorization"),

                 offsetof(ngx_http_headers_in_t, authorization),

                 ngx_http_process_unique_header_line },

    { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),

                 ngx_http_process_header_line },

#if (NGX_HTTP_X_FORWARDED_FOR)

    { ngx_string("X-Forwarded-For"),

                 offsetof(ngx_http_headers_in_t, x_forwarded_for),

                 ngx_http_process_multi_header_lines },

#endif

#if (NGX_HTTP_REALIP)

    { ngx_string("X-Real-IP"),

                 offsetof(ngx_http_headers_in_t, x_real_ip),

                 ngx_http_process_header_line },

#endif

#if (NGX_HTTP_HEADERS)

    { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),

                 ngx_http_process_header_line },

    { ngx_string("Accept-Language"),

                 offsetof(ngx_http_headers_in_t, accept_language),

                 ngx_http_process_header_line },

#endif

#if (NGX_HTTP_DAV)

    { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),

                 ngx_http_process_header_line },

    { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),

                 ngx_http_process_header_line },

    { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),

                 ngx_http_process_header_line },

    { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),

                 ngx_http_process_header_line },

#endif

    { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies),

                 ngx_http_process_multi_header_lines },

    { ngx_null_string, 0, NULL }

};

void

‌ngx_http_init_connection(ngx_connection_t *c)

{

    ngx_uint_t              i;

    ngx_event_t            *rev;

    struct sockaddr_in     *sin;

    ngx_http_port_t        *port;

    ngx_http_in_addr_t     *addr;

    ngx_http_log_ctx_t     *ctx;

    ngx_http_connection_t  *hc;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6    *sin6;

    ngx_http_in6_addr_t    *addr6;

#endif

    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));

    if (hc == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    c->data = hc;

    /* find the server configuration for the address:port */

    port = c->listening->servers;

    if (port->naddrs > 1) {

        /*

         * there are several addresses on this port and one of them

         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()

         * is required to determine a server address

         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {

            ngx_http_close_connection(c);

            return;

        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {

                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {

                    break;

                }

            }

            hc->addr_conf = &addr6[i].conf;

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) c->local_sockaddr;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {

                if (addr[i].addr == sin->sin_addr.s_addr) {

                    break;

                }

            }

            hc->addr_conf = &addr[i].conf;

            break;

        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            addr6 = port->addrs;

            hc->addr_conf = &addr6[0].conf;

            break;

#endif

        default: /* AF_INET */

            addr = port->addrs;

            hc->addr_conf = &addr[0].conf;

            break;

        }

    }

    /* the default server configuration for the address:port */

    hc->conf_ctx = hc->addr_conf->default_server->ctx;

    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));

    if (ctx == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    ctx->connection = c;

    ctx->request = NULL;

    ctx->current_request = NULL;

    c->log->connection = c->number;

    c->log->handler = ngx_http_log_error;

    c->log->data = ctx;

    c->log->action = "waiting for request";

    c->log_error = NGX_ERROR_INFO;

    rev = c->read;

    rev->handler = ngx_http_wait_request_handler;

    c->write->handler = ngx_http_empty_handler;

#if (NGX_HTTP_SPDY)

    if (hc->addr_conf->spdy) {

        rev->handler = ngx_http_spdy_init;

    }

#endif

#if (NGX_HTTP_SSL)

    {

    ngx_http_ssl_srv_conf_t  *sscf;

    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);

    if (sscf->enable || hc->addr_conf->ssl) {

        c->log->action = "SSL handshaking";

        if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {

            ngx_log_error(NGX_LOG_ERR, c->log, 0,

                          "no \"ssl_certificate\" is defined "

                          "in server listening on SSL port");

            ngx_http_close_connection(c);

            return;

        }

        hc->ssl = 1;

        rev->handler = ngx_http_ssl_handshake;

    }

    }

#endif

    if (hc->addr_conf->proxy_protocol) {

        hc->proxy_protocol = 1;

        c->log->action = "reading PROXY protocol";

    }

    if (rev->ready) {

        /* the deferred accept(), rtsig, aio, iocp */

        if (ngx_use_accept_mutex) {

            ngx_post_event(rev, &ngx_posted_events);

            return;

        }

        rev->handler(rev);

        return;

    }

    ngx_add_timer(rev, c->listening->post_accept_timeout);

    ngx_reusable_connection(c, 1);

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_connection(c);

        return;

    }

}

static void

‌ngx_http_wait_request_handler(ngx_event_t *rev)

{

    u_char                    *p;

    size_t                     size;

    ssize_t                    n;

    ngx_buf_t                 *b;

    ngx_connection_t          *c;

    ngx_http_connection_t     *hc;

    ngx_http_core_srv_conf_t  *cscf;

    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_http_close_connection(c);

        return;

    }

    if (c->close) {

        ngx_http_close_connection(c);

        return;

    }

    hc = c->data;

    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    size = cscf->client_header_buffer_size;

    b = c->buffer;

    if (b == NULL) {

        b = ngx_create_temp_buf(c->pool, size);

        if (b == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        c->buffer = b;

    } else if (b->start == NULL) {

        b->start = ngx_palloc(c->pool, size);

        if (b->start == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        b->pos = b->start;

        b->last = b->start;

        b->end = b->last + size;

    }

    n = c->recv(c, b->last, size);

    if (n == NGX_AGAIN) {

        if (!rev->timer_set) {

            ngx_add_timer(rev, c->listening->post_accept_timeout);

            ngx_reusable_connection(c, 1);

        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {

            ngx_http_close_connection(c);

            return;

        }

        /*

         * We are trying to not hold c->buffer's memory for an idle connection.

         */

        if (ngx_pfree(c->pool, b->start) == NGX_OK) {

            b->start = NULL;

        }

        return;

    }

    if (n == NGX_ERROR) {

        ngx_http_close_connection(c);

        return;

    }

    if (n == 0) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client closed connection");

        ngx_http_close_connection(c);

        return;

    }

    b->last += n;

    if (hc->proxy_protocol) {

        hc->proxy_protocol = 0;

        p = ngx_proxy_protocol_parse(c, b->pos, b->last);

        if (p == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        b->pos = p;

        if (b->pos == b->last) {

            c->log->action = "waiting for request";

            b->pos = b->start;

            b->last = b->start;

            ngx_post_event(rev, &ngx_posted_events);

            return;

        }

    }

    c->log->action = "reading client request line";

    ngx_reusable_connection(c, 0);

    c->data = ngx_http_create_request(c);

    if (c->data == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    rev->handler = ngx_http_process_request_line;

    ngx_http_process_request_line(rev);

}

ngx_http_request_t *

‌ngx_http_create_request(ngx_connection_t *c)

{

    ngx_pool_t                 *pool;

    ngx_time_t                 *tp;

    ngx_http_request_t         *r;

    ngx_http_log_ctx_t         *ctx;

    ngx_http_connection_t      *hc;

    ngx_http_core_srv_conf_t   *cscf;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_core_main_conf_t  *cmcf;

    c->requests++;

    hc = c->data;

    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    pool = ngx_create_pool(cscf->request_pool_size, c->log);

    if (pool == NULL) {

        return NULL;

    }

    r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));

    if (r == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    r->pool = pool;

    r->http_connection = hc;

    r->signature = NGX_HTTP_MODULE;

    r->connection = c;

    r->main_conf = hc->conf_ctx->main_conf;

    r->srv_conf = hc->conf_ctx->srv_conf;

    r->loc_conf = hc->conf_ctx->loc_conf;

    r->read_event_handler = ngx_http_block_reading;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    ngx_http_set_connection_log(r->connection, clcf->error_log);

    r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;

    if (ngx_list_init(&r->headers_out.headers, r->pool, 20,

                      sizeof(ngx_table_elt_t))

        != NGX_OK)

    {

        ngx_destroy_pool(r->pool);

        return NULL;

    }

    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);

    if (r->ctx == NULL) {

        ngx_destroy_pool(r->pool);

        return NULL;

    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts

                                        * sizeof(ngx_http_variable_value_t));

    if (r->variables == NULL) {

        ngx_destroy_pool(r->pool);

        return NULL;

    }

#if (NGX_HTTP_SSL)

    if (c->ssl) {

        r->main_filter_need_in_memory = 1;

    }

#endif

    r->main = r;

    r->count = 1;

    tp = ngx_timeofday();

    r->start_sec = tp->sec;

    r->start_msec = tp->msec;

    r->method = NGX_HTTP_UNKNOWN;

    r->http_version = NGX_HTTP_VERSION_10;

    r->headers_in.content_length_n = -1;

    r->headers_in.keep_alive_n = -1;

    r->headers_out.content_length_n = -1;

    r->headers_out.last_modified_time = -1;

    r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;

    r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;

    r->http_state = NGX_HTTP_READING_REQUEST_STATE;

    ctx = c->log->data;

    ctx->request = r;

    ctx->current_request = r;

    r->log_handler = ngx_http_log_error_handler;

#if (NGX_STAT_STUB)

    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);

    r->stat_reading = 1;

    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);

#endif

    return r;

}

#if (NGX_HTTP_SSL)

static void

‌ngx_http_ssl_handshake(ngx_event_t *rev)

{

    u_char                   *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];

    size_t                    size;

    ssize_t                   n;

    ngx_err_t                 err;

    ngx_int_t                 rc;

    ngx_connection_t         *c;

    ngx_http_connection_t    *hc;

    ngx_http_ssl_srv_conf_t  *sscf;

    c = rev->data;

    hc = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,

                   "http check ssl handshake");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_http_close_connection(c);

        return;

    }

    if (c->close) {

        ngx_http_close_connection(c);

        return;

    }

    size = hc->proxy_protocol ? sizeof(buf) : 1;

    n = recv(c->fd, (char *) buf, size, MSG_PEEK);

    err = ngx_socket_errno;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %d", n);

    if (n == -1) {

        if (err == NGX_EAGAIN) {

            if (!rev->timer_set) {

                ngx_add_timer(rev, c->listening->post_accept_timeout);

                ngx_reusable_connection(c, 1);

            }

            if (ngx_handle_read_event(rev, 0) != NGX_OK) {

                ngx_http_close_connection(c);

            }

            return;

        }

        ngx_connection_error(c, err, "recv() failed");

        ngx_http_close_connection(c);

        return;

    }

    if (hc->proxy_protocol) {

        hc->proxy_protocol = 0;

        p = ngx_proxy_protocol_parse(c, buf, buf + n);

        if (p == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        size = p - buf;

        if (c->recv(c, buf, size) != (ssize_t) size) {

            ngx_http_close_connection(c);

            return;

        }

        c->log->action = "SSL handshaking";

        if (n == (ssize_t) size) {

            ngx_post_event(rev, &ngx_posted_events);

            return;

        }

        n = 1;

        buf[0] = *p;

    }

    if (n == 1) {

        if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,

                           "https ssl handshake: 0x%02Xd", buf[0]);

            sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,

                                                ngx_http_ssl_module);

            if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)

                != NGX_OK)

            {

                ngx_http_close_connection(c);

                return;

            }

            rc = ngx_ssl_handshake(c);

            if (rc == NGX_AGAIN) {

                if (!rev->timer_set) {

                    ngx_add_timer(rev, c->listening->post_accept_timeout);

                }

                ngx_reusable_connection(c, 0);

                c->ssl->handler = ngx_http_ssl_handshake_handler;

                return;

            }

            ngx_http_ssl_handshake_handler(c);

            return;

        }

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http");

        c->log->action = "waiting for request";

        rev->handler = ngx_http_wait_request_handler;

        ngx_http_wait_request_handler(rev);

        return;

    }

    ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection");

    ngx_http_close_connection(c);

}

static void

‌ngx_http_ssl_handshake_handler(ngx_connection_t *c)

{

    if (c->ssl->handshaked) {

        /*

         * The majority of browsers do not send the "close notify" alert.

         * Among them are MSIE, old Mozilla, Netscape 4, Konqueror,

         * and Links.  And what is more, MSIE ignores the server's alert.

         *

         * Opera and recent Mozilla send the alert.

         */

        c->ssl->no_wait_shutdown = 1;

#if (NGX_HTTP_SPDY                                                            \

     && (defined TLSEXT_TYPE_application_layer_protocol_negotiation           \

         || defined TLSEXT_TYPE_next_proto_neg))

        {

        unsigned int             len;

        const unsigned char     *data;

        static const ngx_str_t   spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED);

#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation

        SSL_get0_alpn_selected(c->ssl->connection, &data, &len);

#ifdef TLSEXT_TYPE_next_proto_neg

        if (len == 0) {

            SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);

        }

#endif

#else /* TLSEXT_TYPE_next_proto_neg */

        SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);

#endif

        if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) {

            ngx_http_spdy_init(c->read);

            return;

        }

        }

#endif

        c->log->action = "waiting for request";

        c->read->handler = ngx_http_wait_request_handler;

        /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;

        ngx_reusable_connection(c, 1);

        ngx_http_wait_request_handler(c->read);

        return;

    }

    if (c->read->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

    }

    ngx_http_close_connection(c);

}

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

int

‌ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)

{

    ngx_str_t                  host;

    const char                *servername;

    ngx_connection_t          *c;

    ngx_http_connection_t     *hc;

    ngx_http_ssl_srv_conf_t   *sscf;

    ngx_http_core_loc_conf_t  *clcf;

    ngx_http_core_srv_conf_t  *cscf;

    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);

    if (servername == NULL) {

        return SSL_TLSEXT_ERR_NOACK;

    }

    c = ngx_ssl_get_connection(ssl_conn);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "SSL server name: \"%s\"", servername);

    host.len = ngx_strlen(servername);

    if (host.len == 0) {

        return SSL_TLSEXT_ERR_NOACK;

    }

    host.data = (u_char *) servername;

    if (ngx_http_validate_host(&host, c->pool, 1) != NGX_OK) {

        return SSL_TLSEXT_ERR_NOACK;

    }

    hc = c->data;

    if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,

                                     NULL, &cscf)

        != NGX_OK)

    {

        return SSL_TLSEXT_ERR_NOACK;

    }

    hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));

    if (hc->ssl_servername == NULL) {

        return SSL_TLSEXT_ERR_NOACK;

    }

    *hc->ssl_servername = host;

    hc->conf_ctx = cscf->ctx;

    clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);

    ngx_http_set_connection_log(c, clcf->error_log);

    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);

    if (sscf->ssl.ctx) {

        SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);

        /*

         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d

         * adjust other things we care about

         */

        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),

                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));

        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));

#ifdef SSL_CTRL_CLEAR_OPTIONS

        /* only in 0.9.8m+ */

        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &

                                    ~SSL_CTX_get_options(sscf->ssl.ctx));

#endif

        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));

    }

    return SSL_TLSEXT_ERR_OK;

}

#endif

#endif

static void

‌ngx_http_process_request_line(ngx_event_t *rev)

{

    ssize_t              n;

    ngx_int_t            rc, rv;

    ngx_str_t            host;

    ngx_connection_t    *c;

    ngx_http_request_t  *r;

    c = rev->data;

    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,

                   "http process request line");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {

            n = ngx_http_read_request_header(r);

            if (n == NGX_AGAIN || n == NGX_ERROR) {

                return;

            }

        }

        rc = ngx_http_parse_request_line(r, r->header_in);

        if (rc == NGX_OK) {

            /* the request line has been parsed successfully */

            r->request_line.len = r->request_end - r->request_start;

            r->request_line.data = r->request_start;

            r->request_length = r->header_in->pos - r->request_start;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http request line: \"%V\"", &r->request_line);

            r->method_name.len = r->method_end - r->request_start + 1;

            r->method_name.data = r->request_line.data;

            if (r->http_protocol.data) {

                r->http_protocol.len = r->request_end - r->http_protocol.data;

            }

            if (ngx_http_process_request_uri(r) != NGX_OK) {

                return;

            }

            if (r->host_start && r->host_end) {

                host.len = r->host_end - r->host_start;

                host.data = r->host_start;

                rc = ngx_http_validate_host(&host, r->pool, 0);

                if (rc == NGX_DECLINED) {

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,

                                  "client sent invalid host in request line");

                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

                    return;

                }

                if (rc == NGX_ERROR) {

                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                    return;

                }

                if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {

                    return;

                }

                r->headers_in.server = host;

            }

            if (r->http_version < NGX_HTTP_VERSION_10) {

                if (r->headers_in.server.len == 0

                    && ngx_http_set_virtual_server(r, &r->headers_in.server)

                       == NGX_ERROR)

                {

                    return;

                }

                ngx_http_process_request(r);

                return;

            }

            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,

                              sizeof(ngx_table_elt_t))

                != NGX_OK)

            {

                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            c->log->action = "reading client request headers";

            rev->handler = ngx_http_process_request_headers;

            ngx_http_process_request_headers(rev);

            return;

        }

        if (rc != NGX_AGAIN) {

            /* there was error while a request line parsing */

            ngx_log_error(NGX_LOG_INFO, c->log, 0,

                          ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);

            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

            return;

        }

        /* NGX_AGAIN: a request line parsing is still incomplete */

        if (r->header_in->pos == r->header_in->end) {

            rv = ngx_http_alloc_large_header_buffer(r, 1);

            if (rv == NGX_ERROR) {

                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            if (rv == NGX_DECLINED) {

                r->request_line.len = r->header_in->end - r->request_start;

                r->request_line.data = r->request_start;

                ngx_log_error(NGX_LOG_INFO, c->log, 0,

                              "client sent too long URI");

                ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);

                return;

            }

        }

    }

}

ngx_int_t

‌ngx_http_process_request_uri(ngx_http_request_t *r)

{

    ngx_http_core_srv_conf_t  *cscf;

    if (r->args_start) {

        r->uri.len = r->args_start - 1 - r->uri_start;

    } else {

        r->uri.len = r->uri_end - r->uri_start;

    }

    if (r->complex_uri || r->quoted_uri) {

        r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);

        if (r->uri.data == NULL) {

            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_ERROR;

        }

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {

            r->uri.len = 0;

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client sent invalid request");

            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

            return NGX_ERROR;

        }

    } else {

        r->uri.data = r->uri_start;

    }

    r->unparsed_uri.len = r->uri_end - r->uri_start;

    r->unparsed_uri.data = r->uri_start;

    r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;

    if (r->uri_ext) {

        if (r->args_start) {

            r->exten.len = r->args_start - 1 - r->uri_ext;

        } else {

            r->exten.len = r->uri_end - r->uri_ext;

        }

        r->exten.data = r->uri_ext;

    }

    if (r->args_start && r->uri_end > r->args_start) {

        r->args.len = r->uri_end - r->args_start;

        r->args.data = r->args_start;

    }

#if (NGX_WIN32)

    {

    u_char  *p, *last;

    p = r->uri.data;

    last = r->uri.data + r->uri.len;

    while (p < last) {

        if (*p++ == ':') {

            /*

             * this check covers "::$data", "::$index_allocation" and

             * ":$i30:$index_allocation"

             */

            if (p < last && *p == '$') {

                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                              "client sent unsafe win32 URI");

                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

                return NGX_ERROR;

            }

        }

    }

    p = r->uri.data + r->uri.len - 1;

    while (p > r->uri.data) {

        if (*p == ' ') {

            p--;

            continue;

        }

        if (*p == '.') {

            p--;

            continue;

        }

        break;

    }

    if (p != r->uri.data + r->uri.len - 1) {

        r->uri.len = p + 1 - r->uri.data;

        ngx_http_set_exten(r);

    }

    }

#endif

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http uri: \"%V\"", &r->uri);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http args: \"%V\"", &r->args);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http exten: \"%V\"", &r->exten);

    return NGX_OK;

}

static void

‌ngx_http_process_request_headers(ngx_event_t *rev)

{

    u_char                     *p;

    size_t                      len;

    ssize_t                     n;

    ngx_int_t                   rc, rv;

    ngx_table_elt_t            *h;

    ngx_connection_t           *c;

    ngx_http_header_t          *hh;

    ngx_http_request_t         *r;

    ngx_http_core_srv_conf_t   *cscf;

    ngx_http_core_main_conf_t  *cmcf;

    c = rev->data;

    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,

                   "http process request header line");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {

            if (r->header_in->pos == r->header_in->end) {

                rv = ngx_http_alloc_large_header_buffer(r, 0);

                if (rv == NGX_ERROR) {

                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                    return;

                }

                if (rv == NGX_DECLINED) {

                    p = r->header_name_start;

                    r->lingering_close = 1;

                    if (p == NULL) {

                        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                                      "client sent too large request");

                        ngx_http_finalize_request(r,

                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);

                        return;

                    }

                    len = r->header_in->end - p;

                    if (len > NGX_MAX_ERROR_STR - 300) {

                        len = NGX_MAX_ERROR_STR - 300;

                    }

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,

                                "client sent too long header line: \"%*s...\"",

                                len, r->header_name_start);

                    ngx_http_finalize_request(r,

                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);

                    return;

                }

            }

            n = ngx_http_read_request_header(r);

            if (n == NGX_AGAIN || n == NGX_ERROR) {

                return;

            }

        }

        /* the host header could change the server configuration context */

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        rc = ngx_http_parse_header_line(r, r->header_in,

                                        cscf->underscores_in_headers);

        if (rc == NGX_OK) {

            r->request_length += r->header_in->pos - r->header_name_start;

            if (r->invalid_header && cscf->ignore_invalid_headers) {

                /* there was error while a header line parsing */

                ngx_log_error(NGX_LOG_INFO, c->log, 0,

                              "client sent invalid header line: \"%*s\"",

                              r->header_end - r->header_name_start,

                              r->header_name_start);

                continue;

            }

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->headers_in.headers);

            if (h == NULL) {

                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;

            h->key.data = r->header_name_start;

            h->key.data[h->key.len] = '\0';

            h->value.len = r->header_end - r->header_start;

            h->value.data = r->header_start;

            h->value.data[h->value.len] = '\0';

            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);

            if (h->lowcase_key == NULL) {

                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            if (h->key.len == r->lowcase_index) {

                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {

                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);

            }

            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,

                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {

                return;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http header: \"%V: %V\"",

                           &h->key, &h->value);

            continue;

        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http header done");

            r->request_length += r->header_in->pos - r->header_name_start;

            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {

                return;

            }

            ngx_http_process_request(r);

            return;

        }

        if (rc == NGX_AGAIN) {

            /* a header line parsing is still not complete */

            continue;

        }

        /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client sent invalid header line: \"%*s\\r...\"",

                      r->header_end - r->header_name_start,

                      r->header_name_start);

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return;

    }

}

static ssize_t

‌ngx_http_read_request_header(ngx_http_request_t *r)

{

    ssize_t                    n;

    ngx_event_t               *rev;

    ngx_connection_t          *c;

    ngx_http_core_srv_conf_t  *cscf;

    c = r->connection;

    rev = c->read;

    n = r->header_in->last - r->header_in->pos;

    if (n > 0) {

        return n;

    }

    if (rev->ready) {

        n = c->recv(c, r->header_in->last,

                    r->header_in->end - r->header_in->last);

    } else {

        n = NGX_AGAIN;

    }

    if (n == NGX_AGAIN) {

        if (!rev->timer_set) {

            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

            ngx_add_timer(rev, cscf->client_header_timeout);

        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {

            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_ERROR;

        }

        return NGX_AGAIN;

    }

    if (n == 0) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client prematurely closed connection");

    }

    if (n == 0 || n == NGX_ERROR) {

        c->error = 1;

        c->log->action = "reading client request headers";

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return NGX_ERROR;

    }

    r->header_in->last += n;

    return n;

}

static ngx_int_t

‌ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,

    ngx_uint_t request_line)

{

    u_char                    *old, *new;

    ngx_buf_t                 *b;

    ngx_http_connection_t     *hc;

    ngx_http_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http alloc large header buffer");

    if (request_line && r->state == 0) {

        /* the client fills up the buffer with "\r\n" */

        r->header_in->pos = r->header_in->start;

        r->header_in->last = r->header_in->start;

        return NGX_OK;

    }

    old = request_line ? r->request_start : r->header_name_start;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    if (r->state != 0

        && (size_t) (r->header_in->pos - old)

                                     >= cscf->large_client_header_buffers.size)

    {

        return NGX_DECLINED;

    }

    hc = r->http_connection;

    if (hc->nfree) {

        b = hc->free[--hc->nfree];

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http large header free: %p %uz",

                       b->pos, b->end - b->last);

    } else if (hc->nbusy < cscf->large_client_header_buffers.num) {

        if (hc->busy == NULL) {

            hc->busy = ngx_palloc(r->connection->pool,

                  cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));

            if (hc->busy == NULL) {

                return NGX_ERROR;

            }

        }

        b = ngx_create_temp_buf(r->connection->pool,

                                cscf->large_client_header_buffers.size);

        if (b == NULL) {

            return NGX_ERROR;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http large header alloc: %p %uz",

                       b->pos, b->end - b->last);

    } else {

        return NGX_DECLINED;

    }

    hc->busy[hc->nbusy++] = b;

    if (r->state == 0) {

        /*

         * r->state == 0 means that a header line was parsed successfully

         * and we do not need to copy incomplete header line and

         * to relocate the parser header pointers

         */

        r->header_in = b;

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http large header copy: %d", r->header_in->pos - old);

    new = b->start;

    ngx_memcpy(new, old, r->header_in->pos - old);

    b->pos = new + (r->header_in->pos - old);

    b->last = new + (r->header_in->pos - old);

    if (request_line) {

        r->request_start = new;

        if (r->request_end) {

            r->request_end = new + (r->request_end - old);

        }

        r->method_end = new + (r->method_end - old);

        r->uri_start = new + (r->uri_start - old);

        r->uri_end = new + (r->uri_end - old);

        if (r->schema_start) {

            r->schema_start = new + (r->schema_start - old);

            r->schema_end = new + (r->schema_end - old);

        }

        if (r->host_start) {

            r->host_start = new + (r->host_start - old);

            if (r->host_end) {

                r->host_end = new + (r->host_end - old);

            }

        }

        if (r->port_start) {

            r->port_start = new + (r->port_start - old);

            r->port_end = new + (r->port_end - old);

        }

        if (r->uri_ext) {

            r->uri_ext = new + (r->uri_ext - old);

        }

        if (r->args_start) {

            r->args_start = new + (r->args_start - old);

        }

        if (r->http_protocol.data) {

            r->http_protocol.data = new + (r->http_protocol.data - old);

        }

    } else {

        r->header_name_start = new;

        r->header_name_end = new + (r->header_name_end - old);

        r->header_start = new + (r->header_start - old);

        r->header_end = new + (r->header_end - old);

    }

    r->header_in = b;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_table_elt_t  **ph;

    ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);

    if (*ph == NULL) {

        *ph = h;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_table_elt_t  **ph;

    ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);

    if (*ph == NULL) {

        *ph = h;

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                  "client sent duplicate header line: \"%V: %V\", "

                  "previous value: \"%V: %V\"",

                  &h->key, &h->value, &(*ph)->key, &(*ph)->value);

    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_int_t  rc;

    ngx_str_t  host;

    if (r->headers_in.host == NULL) {

        r->headers_in.host = h;

    }

    host = h->value;

    rc = ngx_http_validate_host(&host, r->pool, 0);

    if (rc == NGX_DECLINED) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent invalid host header");

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return NGX_ERROR;

    }

    if (rc == NGX_ERROR) {

        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_ERROR;

    }

    if (r->headers_in.server.len) {

        return NGX_OK;

    }

    if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {

        return NGX_ERROR;

    }

    r->headers_in.server = host;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) {

        r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;

    } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) {

        r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    u_char  *user_agent, *msie;

    if (r->headers_in.user_agent) {

        return NGX_OK;

    }

    r->headers_in.user_agent = h;

    /* check some widespread browsers while the header is in CPU cache */

    user_agent = h->value.data;

    msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1);

    if (msie && msie + 7 < user_agent + h->value.len) {

        r->headers_in.msie = 1;

        if (msie[6] == '.') {

            switch (msie[5]) {

            case '4':

            case '5':

                r->headers_in.msie6 = 1;

                break;

            case '6':

                if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) {

                    r->headers_in.msie6 = 1;

                }

                break;

            }

        }

#if 0

        /* MSIE ignores the SSL "close notify" alert */

        if (c->ssl) {

            c->ssl->no_send_shutdown = 1;

        }

#endif

    }

    if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {

        r->headers_in.opera = 1;

        r->headers_in.msie = 0;

        r->headers_in.msie6 = 0;

    }

    if (!r->headers_in.msie && !r->headers_in.opera) {

        if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) {

            r->headers_in.gecko = 1;

        } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) {

            r->headers_in.chrome = 1;

        } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)

                   && ngx_strstrn(user_agent, "Mac OS X", 8 - 1))

        {

            r->headers_in.safari = 1;

        } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) {

            r->headers_in.konqueror = 1;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_process_multi_header_lines(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_array_t       *headers;

    ngx_table_elt_t  **ph;

    headers = (ngx_array_t *) ((char *) &r->headers_in + offset);

    if (headers->elts == NULL) {

        if (ngx_array_init(headers, r->pool, 1, sizeof(ngx_table_elt_t *))

            != NGX_OK)

        {

            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_ERROR;

        }

    }

    ph = ngx_array_push(headers);

    if (ph == NULL) {

        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_ERROR;

    }

    *ph = h;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_process_request_header(ngx_http_request_t *r)

{

    if (r->headers_in.server.len == 0

        && ngx_http_set_virtual_server(r, &r->headers_in.server)

           == NGX_ERROR)

    {

        return NGX_ERROR;

    }

    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                   "client sent HTTP/1.1 request without \"Host\" header");

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

        return NGX_ERROR;

    }

    if (r->headers_in.content_length) {

        r->headers_in.content_length_n =

                            ngx_atoof(r->headers_in.content_length->value.data,

                                      r->headers_in.content_length->value.len);

        if (r->headers_in.content_length_n == NGX_ERROR) {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client sent invalid \"Content-Length\" header");

            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

            return NGX_ERROR;

        }

    }

    if (r->method & NGX_HTTP_TRACE) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent TRACE method");

        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);

        return NGX_ERROR;

    }

    if (r->headers_in.transfer_encoding) {

        if (r->headers_in.transfer_encoding->value.len == 7

            && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,

                               (u_char *) "chunked", 7) == 0)

        {

            r->headers_in.content_length = NULL;

            r->headers_in.content_length_n = -1;

            r->headers_in.chunked = 1;

        } else if (r->headers_in.transfer_encoding->value.len != 8

            || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,

                               (u_char *) "identity", 8) != 0)

        {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client sent unknown \"Transfer-Encoding\": \"%V\"",

                          &r->headers_in.transfer_encoding->value);

            ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);

            return NGX_ERROR;

        }

    }

    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {

        if (r->headers_in.keep_alive) {

            r->headers_in.keep_alive_n =

                            ngx_atotm(r->headers_in.keep_alive->value.data,

                                      r->headers_in.keep_alive->value.len);

        }

    }

    return NGX_OK;

}

void

‌ngx_http_process_request(ngx_http_request_t *r)

{

    ngx_connection_t  *c;

    c = r->connection;

#if (NGX_HTTP_SSL)

    if (r->http_connection->ssl) {

        long                      rc;

        X509                     *cert;

        ngx_http_ssl_srv_conf_t  *sscf;

        if (c->ssl == NULL) {

            ngx_log_error(NGX_LOG_INFO, c->log, 0,

                          "client sent plain HTTP request to HTTPS port");

            ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);

            return;

        }

        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);

        if (sscf->verify) {

            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK

                && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))

            {

                ngx_log_error(NGX_LOG_INFO, c->log, 0,

                              "client SSL certificate verify error: (%l:%s)",

                              rc, X509_verify_cert_error_string(rc));

                ngx_ssl_remove_cached_session(sscf->ssl.ctx,

                                       (SSL_get0_session(c->ssl->connection)));

                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);

                return;

            }

            if (sscf->verify == 1) {

                cert = SSL_get_peer_certificate(c->ssl->connection);

                if (cert == NULL) {

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,

                                  "client sent no required SSL certificate");

                    ngx_ssl_remove_cached_session(sscf->ssl.ctx,

                                       (SSL_get0_session(c->ssl->connection)));

                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);

                    return;

                }

                X509_free(cert);

            }

        }

    }

#endif

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

#if (NGX_STAT_STUB)

    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);

    r->stat_reading = 0;

    (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);

    r->stat_writing = 1;

#endif

    c->read->handler = ngx_http_request_handler;

    c->write->handler = ngx_http_request_handler;

    r->read_event_handler = ngx_http_block_reading;

    ngx_http_handler(r);

    ngx_http_run_posted_requests(c);

}

static ngx_int_t

‌ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)

{

    u_char  *h, ch;

    size_t   i, dot_pos, host_len;

    enum {

        sw_usual = 0,

        sw_literal,

        sw_rest

    } state;

    dot_pos = host->len;

    host_len = host->len;

    h = host->data;

    state = sw_usual;

    for (i = 0; i < host->len; i++) {

        ch = h[i];

        switch (ch) {

        case '.':

            if (dot_pos == i - 1) {

                return NGX_DECLINED;

            }

            dot_pos = i;

            break;

        case ':':

            if (state == sw_usual) {

                host_len = i;

                state = sw_rest;

            }

            break;

        case '[':

            if (i == 0) {

                state = sw_literal;

            }

            break;

        case ']':

            if (state == sw_literal) {

                host_len = i + 1;

                state = sw_rest;

            }

            break;

        case '\0':

            return NGX_DECLINED;

        default:

            if (ngx_path_separator(ch)) {

                return NGX_DECLINED;

            }

            if (ch >= 'A' && ch <= 'Z') {

                alloc = 1;

            }

            break;

        }

    }

    if (dot_pos == host_len - 1) {

        host_len--;

    }

    if (host_len == 0) {

        return NGX_DECLINED;

    }

    if (alloc) {

        host->data = ngx_pnalloc(pool, host_len);

        if (host->data == NULL) {

            return NGX_ERROR;

        }

        ngx_strlow(host->data, h, host_len);

    }

    host->len = host_len;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)

{

    ngx_int_t                  rc;

    ngx_http_connection_t     *hc;

    ngx_http_core_loc_conf_t  *clcf;

    ngx_http_core_srv_conf_t  *cscf;

#if (NGX_SUPPRESS_WARN)

    cscf = NULL;

#endif

    hc = r->http_connection;

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

    if (hc->ssl_servername) {

        if (hc->ssl_servername->len == host->len

            && ngx_strncmp(hc->ssl_servername->data,

                           host->data, host->len) == 0)

        {

#if (NGX_PCRE)

            if (hc->ssl_servername_regex

                && ngx_http_regex_exec(r, hc->ssl_servername_regex,

                                          hc->ssl_servername) != NGX_OK)

            {

                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return NGX_ERROR;

            }

#endif

            return NGX_OK;

        }

    }

#endif

    rc = ngx_http_find_virtual_server(r->connection,

                                      hc->addr_conf->virtual_names,

                                      host, r, &cscf);

    if (rc == NGX_ERROR) {

        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return NGX_ERROR;

    }

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

    if (hc->ssl_servername) {

        ngx_http_ssl_srv_conf_t  *sscf;

        if (rc == NGX_DECLINED) {

            cscf = hc->addr_conf->default_server;

            rc = NGX_OK;

        }

        sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);

        if (sscf->verify) {

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                          "client attempted to request the server name "

                          "different from that one was negotiated");

            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);

            return NGX_ERROR;

        }

    }

#endif

    if (rc == NGX_DECLINED) {

        return NGX_OK;

    }

    r->srv_conf = cscf->ctx->srv_conf;

    r->loc_conf = cscf->ctx->loc_conf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    ngx_http_set_connection_log(r->connection, clcf->error_log);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_find_virtual_server(ngx_connection_t *c,

    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,

    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)

{

    ngx_http_core_srv_conf_t  *cscf;

    if (virtual_names == NULL) {

        return NGX_DECLINED;

    }

    cscf = ngx_hash_find_combined(&virtual_names->names,

                                  ngx_hash_key(host->data, host->len),

                                  host->data, host->len);

    if (cscf) {

        *cscfp = cscf;

        return NGX_OK;

    }

#if (NGX_PCRE)

    if (host->len && virtual_names->nregex) {

        ngx_int_t                n;

        ngx_uint_t               i;

        ngx_http_server_name_t  *sn;

        sn = virtual_names->regex;

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

        if (r == NULL) {

            ngx_http_connection_t  *hc;

            for (i = 0; i < virtual_names->nregex; i++) {

                n = ngx_regex_exec(sn[i].regex->regex, host, NULL, 0);

                if (n == NGX_REGEX_NO_MATCHED) {

                    continue;

                }

                if (n >= 0) {

                    hc = c->data;

                    hc->ssl_servername_regex = sn[i].regex;

                    *cscfp = sn[i].server;

                    return NGX_OK;

                }

                ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                              ngx_regex_exec_n " failed: %i "

                              "on \"%V\" using \"%V\"",

                              n, host, &sn[i].regex->name);

                return NGX_ERROR;

            }

            return NGX_DECLINED;

        }

#endif /* NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME */

        for (i = 0; i < virtual_names->nregex; i++) {

            n = ngx_http_regex_exec(r, sn[i].regex, host);

            if (n == NGX_DECLINED) {

                continue;

            }

            if (n == NGX_OK) {

                *cscfp = sn[i].server;

                return NGX_OK;

            }

            return NGX_ERROR;

        }

    }

#endif /* NGX_PCRE */

    return NGX_DECLINED;

}

static void

‌ngx_http_request_handler(ngx_event_t *ev)

{

    ngx_connection_t    *c;

    ngx_http_request_t  *r;

    c = ev->data;

    r = c->data;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http run request: \"%V?%V\"", &r->uri, &r->args);

    if (ev->write) {

        r->write_event_handler(r);

    } else {

        r->read_event_handler(r);

    }

    ngx_http_run_posted_requests(c);

}

void

‌ngx_http_run_posted_requests(ngx_connection_t *c)

{

    ngx_http_request_t         *r;

    ngx_http_posted_request_t  *pr;

    for ( ;; ) {

        if (c->destroyed) {

            return;

        }

        r = c->data;

        pr = r->main->posted_requests;

        if (pr == NULL) {

            return;

        }

        r->main->posted_requests = pr->next;

        r = pr->request;

        ngx_http_set_log_request(c->log, r);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "http posted request: \"%V?%V\"", &r->uri, &r->args);

        r->write_event_handler(r);

    }

}

ngx_int_t

‌ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)

{

    ngx_http_posted_request_t  **p;

    if (pr == NULL) {

        pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));

        if (pr == NULL) {

            return NGX_ERROR;

        }

    }

    pr->request = r;

    pr->next = NULL;

    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }

    *p = pr;

    return NGX_OK;

}

void

‌ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_connection_t          *c;

    ngx_http_request_t        *pr;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http finalize request: %d, \"%V?%V\" a:%d, c:%d",

                   rc, &r->uri, &r->args, r == c->data, r->main->count);

    if (rc == NGX_DONE) {

        ngx_http_finalize_connection(r);

        return;

    }

    if (rc == NGX_OK && r->filter_finalize) {

        c->error = 1;

    }

    if (rc == NGX_DECLINED) {

        r->content_handler = NULL;

        r->write_event_handler = ngx_http_core_run_phases;

        ngx_http_core_run_phases(r);

        return;

    }

    if (r != r->main && r->post_subrequest) {

        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);

    }

    if (rc == NGX_ERROR

        || rc == NGX_HTTP_REQUEST_TIME_OUT

        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST

        || c->error)

    {

        if (ngx_http_post_action(r) == NGX_OK) {

            return;

        }

        if (r->main->blocked) {

            r->write_event_handler = ngx_http_request_finalizer;

        }

        ngx_http_terminate_request(r, rc);

        return;

    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE

        || rc == NGX_HTTP_CREATED

        || rc == NGX_HTTP_NO_CONTENT)

    {

        if (rc == NGX_HTTP_CLOSE) {

            ngx_http_terminate_request(r, rc);

            return;

        }

        if (r == r->main) {

            if (c->read->timer_set) {

                ngx_del_timer(c->read);

            }

            if (c->write->timer_set) {

                ngx_del_timer(c->write);

            }

        }

        c->read->handler = ngx_http_request_handler;

        c->write->handler = ngx_http_request_handler;

        ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));

        return;

    }

    if (r != r->main) {

        if (r->buffered || r->postponed) {

            if (ngx_http_set_write_handler(r) != NGX_OK) {

                ngx_http_terminate_request(r, 0);

            }

            return;

        }

        pr = r->parent;

        if (r == c->data) {

            r->main->count--;

            r->main->subrequests++;

            if (!r->logged) {

                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

                if (clcf->log_subrequest) {

                    ngx_http_log_request(r);

                }

                r->logged = 1;

            } else {

                ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                              "subrequest: \"%V?%V\" logged again",

                              &r->uri, &r->args);

            }

            r->done = 1;

            if (pr->postponed && pr->postponed->request == r) {

                pr->postponed = pr->postponed->next;

            }

            c->data = pr;

        } else {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http finalize non-active request: \"%V?%V\"",

                           &r->uri, &r->args);

            r->write_event_handler = ngx_http_request_finalizer;

            if (r->waited) {

                r->done = 1;

            }

        }

        if (ngx_http_post_request(pr, NULL) != NGX_OK) {

            r->main->count++;

            ngx_http_terminate_request(r, 0);

            return;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "http wake parent request: \"%V?%V\"",

                       &pr->uri, &pr->args);

        return;

    }

    if (r->buffered || c->buffered || r->postponed || r->blocked) {

        if (ngx_http_set_write_handler(r) != NGX_OK) {

            ngx_http_terminate_request(r, 0);

        }

        return;

    }

    if (r != c->data) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "http finalize non-active request: \"%V?%V\"",

                      &r->uri, &r->args);

        return;

    }

    r->done = 1;

    r->write_event_handler = ngx_http_request_empty_handler;

    if (!r->post_action) {

        r->request_complete = 1;

    }

    if (ngx_http_post_action(r) == NGX_OK) {

        return;

    }

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    if (c->write->timer_set) {

        c->write->delayed = 0;

        ngx_del_timer(c->write);

    }

    if (c->read->eof) {

        ngx_http_close_request(r, 0);

        return;

    }

    ngx_http_finalize_connection(r);

}

static void

‌ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_http_cleanup_t    *cln;

    ngx_http_request_t    *mr;

    ngx_http_ephemeral_t  *e;

    mr = r->main;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http terminate request count:%d", mr->count);

    if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {

        mr->headers_out.status = rc;

    }

    cln = mr->cleanup;

    mr->cleanup = NULL;

    while (cln) {

        if (cln->handler) {

            cln->handler(cln->data);

        }

        cln = cln->next;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http terminate cleanup count:%d blk:%d",

                   mr->count, mr->blocked);

    if (mr->write_event_handler) {

        if (mr->blocked) {

            return;

        }

        e = ngx_http_ephemeral(mr);

        mr->posted_requests = NULL;

        mr->write_event_handler = ngx_http_terminate_handler;

        (void) ngx_http_post_request(mr, &e->terminal_posted_request);

        return;

    }

    ngx_http_close_request(mr, rc);

}

static void

‌ngx_http_terminate_handler(ngx_http_request_t *r)

{

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http terminate handler count:%d", r->count);

    r->count = 1;

    ngx_http_close_request(r, 0);

}

static void

‌ngx_http_finalize_connection(ngx_http_request_t *r)

{

    ngx_http_core_loc_conf_t  *clcf;

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        ngx_http_close_request(r, 0);

        return;

    }

#endif

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->main->count != 1) {

        if (r->discard_body) {

            r->read_event_handler = ngx_http_discarded_request_body_handler;

            ngx_add_timer(r->connection->read, clcf->lingering_timeout);

            if (r->lingering_time == 0) {

                r->lingering_time = ngx_time()

                                      + (time_t) (clcf->lingering_time / 1000);

            }

        }

        ngx_http_close_request(r, 0);

        return;

    }

    if (!ngx_terminate

         && !ngx_exiting

         && r->keepalive

         && clcf->keepalive_timeout > 0)

    {

        ngx_http_set_keepalive(r);

        return;

    }

    if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS

        || (clcf->lingering_close == NGX_HTTP_LINGERING_ON

            && (r->lingering_close

                || r->header_in->pos < r->header_in->last

                || r->connection->read->ready)))

    {

        ngx_http_set_lingering_close(r);

        return;

    }

    ngx_http_close_request(r, 0);

}

static ngx_int_t

‌ngx_http_set_write_handler(ngx_http_request_t *r)

{

    ngx_event_t               *wev;

    ngx_http_core_loc_conf_t  *clcf;

    r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;

    r->read_event_handler = r->discard_body ?

                                ngx_http_discarded_request_body_handler:

                                ngx_http_test_reading;

    r->write_event_handler = ngx_http_writer;

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        return NGX_OK;

    }

#endif

    wev = r->connection->write;

    if (wev->ready && wev->delayed) {

        return NGX_OK;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!wev->delayed) {

        ngx_add_timer(wev, clcf->send_timeout);

    }

    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {

        ngx_http_close_request(r, 0);

        return NGX_ERROR;

    }

    return NGX_OK;

}

static void

‌ngx_http_writer(ngx_http_request_t *r)

{

    int                        rc;

    ngx_event_t               *wev;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    wev = c->write;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,

                   "http writer handler: \"%V?%V\"", &r->uri, &r->args);

    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);

    if (wev->timedout) {

        if (!wev->delayed) {

            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                          "client timed out");

            c->timedout = 1;

            ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);

            return;

        }

        wev->timedout = 0;

        wev->delayed = 0;

        if (!wev->ready) {

            ngx_add_timer(wev, clcf->send_timeout);

            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {

                ngx_http_close_request(r, 0);

            }

            return;

        }

    }

    if (wev->delayed || r->aio) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,

                       "http writer delayed");

        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {

            ngx_http_close_request(r, 0);

        }

        return;

    }

    rc = ngx_http_output_filter(r, NULL);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http writer output filter: %d, \"%V?%V\"",

                   rc, &r->uri, &r->args);

    if (rc == NGX_ERROR) {

        ngx_http_finalize_request(r, rc);

        return;

    }

    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {

#if (NGX_HTTP_SPDY)

        if (r->spdy_stream) {

            return;

        }

#endif

        if (!wev->delayed) {

            ngx_add_timer(wev, clcf->send_timeout);

        }

        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {

            ngx_http_close_request(r, 0);

        }

        return;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,

                   "http writer done: \"%V?%V\"", &r->uri, &r->args);

    r->write_event_handler = ngx_http_request_empty_handler;

    ngx_http_finalize_request(r, rc);

}

static void

‌ngx_http_request_finalizer(ngx_http_request_t *r)

{

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http finalizer done: \"%V?%V\"", &r->uri, &r->args);

    ngx_http_finalize_request(r, 0);

}

void

‌ngx_http_block_reading(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http reading blocked");

    /* aio does not call this handler */

    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)

        && r->connection->read->active)

    {

        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {

            ngx_http_close_request(r, 0);

        }

    }

}

void

‌ngx_http_test_reading(ngx_http_request_t *r)

{

    int                n;

    char               buf[1];

    ngx_err_t          err;

    ngx_event_t       *rev;

    ngx_connection_t  *c;

    c = r->connection;

    rev = c->read;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        if (c->error) {

            err = 0;

            goto closed;

        }

        return;

    }

#endif

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        if (!rev->pending_eof) {

            return;

        }

        rev->eof = 1;

        c->error = 1;

        err = rev->kq_errno;

        goto closed;

    }

#endif

#if (NGX_HAVE_EPOLLRDHUP)

    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && rev->pending_eof) {

        socklen_t  len;

        rev->eof = 1;

        c->error = 1;

        err = 0;

        len = sizeof(ngx_err_t);

        /*

         * BSDs and Linux return 0 and set a pending error in err

         * Solaris returns -1 and sets errno

         */

        if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)

            == -1)

        {

            err = ngx_socket_errno;

        }

        goto closed;

    }

#endif

    n = recv(c->fd, buf, 1, MSG_PEEK);

    if (n == 0) {

        rev->eof = 1;

        c->error = 1;

        err = 0;

        goto closed;

    } else if (n == -1) {

        err = ngx_socket_errno;

        if (err != NGX_EAGAIN) {

            rev->eof = 1;

            c->error = 1;

            goto closed;

        }

    }

    /* aio does not call this handler */

    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {

        if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {

            ngx_http_close_request(r, 0);

        }

    }

    return;

closed:

    if (err) {

        rev->error = 1;

    }

    ngx_log_error(NGX_LOG_INFO, c->log, err,

                  "client prematurely closed connection");

    ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);

}

static void

‌ngx_http_set_keepalive(ngx_http_request_t *r)

{

    int                        tcp_nodelay;

    ngx_int_t                  i;

    ngx_buf_t                 *b, *f;

    ngx_event_t               *rev, *wev;

    ngx_connection_t          *c;

    ngx_http_connection_t     *hc;

    ngx_http_core_srv_conf_t  *cscf;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    rev = c->read;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");

    if (r->discard_body) {

        r->write_event_handler = ngx_http_request_empty_handler;

        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);

        ngx_add_timer(rev, clcf->lingering_timeout);

        return;

    }

    c->log->action = "closing request";

    hc = r->http_connection;

    b = r->header_in;

    if (b->pos < b->last) {

        /* the pipelined request */

        if (b != c->buffer) {

            /*

             * If the large header buffers were allocated while the previous

             * request processing then we do not use c->buffer for

             * the pipelined request (see ngx_http_create_request()).

             *

             * Now we would move the large header buffers to the free list.

             */

            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

            if (hc->free == NULL) {

                hc->free = ngx_palloc(c->pool,

                  cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));

                if (hc->free == NULL) {

                    ngx_http_close_request(r, 0);

                    return;

                }

            }

            for (i = 0; i < hc->nbusy - 1; i++) {

                f = hc->busy[i];

                hc->free[hc->nfree++] = f;

                f->pos = f->start;

                f->last = f->start;

            }

            hc->busy[0] = b;

            hc->nbusy = 1;

        }

    }

    /* guard against recursive call from ngx_http_finalize_connection() */

    r->keepalive = 0;

    ngx_http_free_request(r, 0);

    c->data = hc;

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_connection(c);

        return;

    }

    wev = c->write;

    wev->handler = ngx_http_empty_handler;

    if (b->pos < b->last) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");

        c->log->action = "reading client pipelined request line";

        r = ngx_http_create_request(c);

        if (r == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        r->pipeline = 1;

        c->data = r;

        c->sent = 0;

        c->destroyed = 0;

        if (rev->timer_set) {

            ngx_del_timer(rev);

        }

        rev->handler = ngx_http_process_request_line;

        ngx_post_event(rev, &ngx_posted_events);

        return;

    }

    /*

     * To keep a memory footprint as small as possible for an idle keepalive

     * connection we try to free c->buffer's memory if it was allocated outside

     * the c->pool.  The large header buffers are always allocated outside the

     * c->pool and are freed too.

     */

    b = c->buffer;

    if (ngx_pfree(c->pool, b->start) == NGX_OK) {

        /*

         * the special note for ngx_http_keepalive_handler() that

         * c->buffer's memory was freed

         */

        b->pos = NULL;

    } else {

        b->pos = b->start;

        b->last = b->start;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p %d",

                   hc->free, hc->nfree);

    if (hc->free) {

        for (i = 0; i < hc->nfree; i++) {

            ngx_pfree(c->pool, hc->free[i]->start);

            hc->free[i] = NULL;

        }

        hc->nfree = 0;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %d",

                   hc->busy, hc->nbusy);

    if (hc->busy) {

        for (i = 0; i < hc->nbusy; i++) {

            ngx_pfree(c->pool, hc->busy[i]->start);

            hc->busy[i] = NULL;

        }

        hc->nbusy = 0;

    }

#if (NGX_HTTP_SSL)

    if (c->ssl) {

        ngx_ssl_free_buffer(c);

    }

#endif

    rev->handler = ngx_http_keepalive_handler;

    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {

        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {

            ngx_http_close_connection(c);

            return;

        }

    }

    c->log->action = "keepalive";

    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {

        if (ngx_tcp_push(c->fd) == -1) {

            ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed");

            ngx_http_close_connection(c);

            return;

        }

        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;

        tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;

    } else {

        tcp_nodelay = 1;

    }

    if (tcp_nodelay

        && clcf->tcp_nodelay

        && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)

    {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");

        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,

                       (const void *) &tcp_nodelay, sizeof(int))

            == -1)

        {

#if (NGX_SOLARIS)

            /* Solaris returns EINVAL if a socket has been shut down */

            c->log_error = NGX_ERROR_IGNORE_EINVAL;

#endif

            ngx_connection_error(c, ngx_socket_errno,

                                 "setsockopt(TCP_NODELAY) failed");

            c->log_error = NGX_ERROR_INFO;

            ngx_http_close_connection(c);

            return;

        }

        c->tcp_nodelay = NGX_TCP_NODELAY_SET;

    }

#if 0

    /* if ngx_http_request_t was freed then we need some other place */

    r->http_state = NGX_HTTP_KEEPALIVE_STATE;

#endif

    c->idle = 1;

    ngx_reusable_connection(c, 1);

    ngx_add_timer(rev, clcf->keepalive_timeout);

    if (rev->ready) {

        ngx_post_event(rev, &ngx_posted_events);

    }

}

static void

‌ngx_http_keepalive_handler(ngx_event_t *rev)

{

    size_t             size;

    ssize_t            n;

    ngx_buf_t         *b;

    ngx_connection_t  *c;

    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");

    if (rev->timedout || c->close) {

        ngx_http_close_connection(c);

        return;

    }

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        if (rev->pending_eof) {

            c->log->handler = NULL;

            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,

                          "kevent() reported that client %V closed "

                          "keepalive connection", &c->addr_text);

#if (NGX_HTTP_SSL)

            if (c->ssl) {

                c->ssl->no_send_shutdown = 1;

            }

#endif

            ngx_http_close_connection(c);

            return;

        }

    }

#endif

    b = c->buffer;

    size = b->end - b->start;

    if (b->pos == NULL) {

        /*

         * The c->buffer's memory was freed by ngx_http_set_keepalive().

         * However, the c->buffer->start and c->buffer->end were not changed

         * to keep the buffer size.

         */

        b->pos = ngx_palloc(c->pool, size);

        if (b->pos == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        b->start = b->pos;

        b->last = b->pos;

        b->end = b->pos + size;

    }

    /*

     * MSIE closes a keepalive connection with RST flag

     * so we ignore ECONNRESET here.

     */

    c->log_error = NGX_ERROR_IGNORE_ECONNRESET;

    ngx_set_socket_errno(0);

    n = c->recv(c, b->last, size);

    c->log_error = NGX_ERROR_INFO;

    if (n == NGX_AGAIN) {

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {

            ngx_http_close_connection(c);

            return;

        }

        /*

         * Like ngx_http_set_keepalive() we are trying to not hold

         * c->buffer's memory for a keepalive connection.

         */

        if (ngx_pfree(c->pool, b->start) == NGX_OK) {

            /*

             * the special note that c->buffer's memory was freed

             */

            b->pos = NULL;

        }

        return;

    }

    if (n == NGX_ERROR) {

        ngx_http_close_connection(c);

        return;

    }

    c->log->handler = NULL;

    if (n == 0) {

        ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno,

                      "client %V closed keepalive connection", &c->addr_text);

        ngx_http_close_connection(c);

        return;

    }

    b->last += n;

    c->log->handler = ngx_http_log_error;

    c->log->action = "reading client request line";

    c->idle = 0;

    ngx_reusable_connection(c, 0);

    c->data = ngx_http_create_request(c);

    if (c->data == NULL) {

        ngx_http_close_connection(c);

        return;

    }

    c->sent = 0;

    c->destroyed = 0;

    ngx_del_timer(rev);

    rev->handler = ngx_http_process_request_line;

    ngx_http_process_request_line(rev);

}

static void

‌ngx_http_set_lingering_close(ngx_http_request_t *r)

{

    ngx_event_t               *rev, *wev;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    rev = c->read;

    rev->handler = ngx_http_lingering_close_handler;

    r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);

    ngx_add_timer(rev, clcf->lingering_timeout);

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_request(r, 0);

        return;

    }

    wev = c->write;

    wev->handler = ngx_http_empty_handler;

    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {

        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {

            ngx_http_close_request(r, 0);

            return;

        }

    }

    if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {

        ngx_connection_error(c, ngx_socket_errno,

                             ngx_shutdown_socket_n " failed");

        ngx_http_close_request(r, 0);

        return;

    }

    if (rev->ready) {

        ngx_http_lingering_close_handler(rev);

    }

}

static void

‌ngx_http_lingering_close_handler(ngx_event_t *rev)

{

    ssize_t                    n;

    ngx_msec_t                 timer;

    ngx_connection_t          *c;

    ngx_http_request_t        *r;

    ngx_http_core_loc_conf_t  *clcf;

    u_char                     buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];

    c = rev->data;

    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http lingering close handler");

    if (rev->timedout) {

        ngx_http_close_request(r, 0);

        return;

    }

    timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();

    if ((ngx_msec_int_t) timer <= 0) {

        ngx_http_close_request(r, 0);

        return;

    }

    do {

        n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %d", n);

        if (n == NGX_ERROR || n == 0) {

            ngx_http_close_request(r, 0);

            return;

        }

    } while (rev->ready);

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_request(r, 0);

        return;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    timer *= 1000;

    if (timer > clcf->lingering_timeout) {

        timer = clcf->lingering_timeout;

    }

    ngx_add_timer(rev, timer);

}

void

‌ngx_http_empty_handler(ngx_event_t *wev)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http empty handler");

    return;

}

void

‌ngx_http_request_empty_handler(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http request empty handler");

    return;

}

ngx_int_t

‌ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags)

{

    ngx_buf_t    *b;

    ngx_chain_t   out;

    b = ngx_calloc_buf(r->pool);

    if (b == NULL) {

        return NGX_ERROR;

    }

    if (flags & NGX_HTTP_LAST) {

        if (r == r->main && !r->post_action) {

            b->last_buf = 1;

        } else {

            b->sync = 1;

            b->last_in_chain = 1;

        }

    }

    if (flags & NGX_HTTP_FLUSH) {

        b->flush = 1;

    }

    out.buf = b;

    out.next = NULL;

    return ngx_http_output_filter(r, &out);

}

static ngx_int_t

‌ngx_http_post_action(ngx_http_request_t *r)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->post_action.data == NULL) {

        return NGX_DECLINED;

    }

    if (r->post_action && r->uri_changes == 0) {

        return NGX_DECLINED;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "post action: \"%V\"", &clcf->post_action);

    r->main->count--;

    r->http_version = NGX_HTTP_VERSION_9;

    r->header_only = 1;

    r->post_action = 1;

    r->read_event_handler = ngx_http_block_reading;

    if (clcf->post_action.data[0] == '/') {

        ngx_http_internal_redirect(r, &clcf->post_action, NULL);

    } else {

        ngx_http_named_location(r, &clcf->post_action);

    }

    return NGX_OK;

}

static void

‌ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_connection_t  *c;

    r = r->main;

    c = r->connection;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http request count:%d blk:%d", r->count, r->blocked);

    if (r->count == 0) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http request count is zero");

    }

    r->count--;

    if (r->count || r->blocked) {

        return;

    }

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        ngx_http_spdy_close_stream(r->spdy_stream, rc);

        return;

    }

#endif

    ngx_http_free_request(r, rc);

    ngx_http_close_connection(c);

}

void

‌ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_t                 *log;

    ngx_pool_t                *pool;

    struct linger              linger;

    ngx_http_cleanup_t        *cln;

    ngx_http_log_ctx_t        *ctx;

    ngx_http_core_loc_conf_t  *clcf;

    log = r->connection->log;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http close request");

    if (r->pool == NULL) {

        ngx_log_error(NGX_LOG_ALERT, log, 0, "http request already closed");

        return;

    }

    cln = r->cleanup;

    r->cleanup = NULL;

    while (cln) {

        if (cln->handler) {

            cln->handler(cln->data);

        }

        cln = cln->next;

    }

#if (NGX_STAT_STUB)

    if (r->stat_reading) {

        (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);

    }

    if (r->stat_writing) {

        (void) ngx_atomic_fetch_add(ngx_stat_writing, -1);

    }

#endif

    if (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) {

        r->headers_out.status = rc;

    }

    log->action = "logging request";

    ngx_http_log_request(r);

    log->action = "closing request";

    if (r->connection->timedout) {

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (clcf->reset_timedout_connection) {

            linger.l_onoff = 1;

            linger.l_linger = 0;

            if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,

                           (const void *) &linger, sizeof(struct linger)) == -1)

            {

                ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,

                              "setsockopt(SO_LINGER) failed");

            }

        }

    }

    /* the various request strings were allocated from r->pool */

    ctx = log->data;

    ctx->request = NULL;

    r->request_line.len = 0;

    r->connection->destroyed = 1;

    /*

     * Setting r->pool to NULL will increase probability to catch double close

     * of request since the request object is allocated from its own pool.

     */

    pool = r->pool;

    r->pool = NULL;

    ngx_destroy_pool(pool);

}

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);

    }

}

void

‌ngx_http_close_connection(ngx_connection_t *c)

{

    ngx_pool_t  *pool;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "close http connection: %d", c->fd);

#if (NGX_HTTP_SSL)

    if (c->ssl) {

        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {

            c->ssl->handler = ngx_http_close_connection;

            return;

        }

    }

#endif

#if (NGX_STAT_STUB)

    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);

#endif

    c->destroyed = 1;

    pool = c->pool;

    ngx_close_connection(c);

    ngx_destroy_pool(pool);

}

static u_char *

‌ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)

{

    u_char              *p;

    ngx_http_request_t  *r;

    ngx_http_log_ctx_t  *ctx;

    if (log->action) {

        p = ngx_snprintf(buf, len, " while %s", log->action);

        len -= p - buf;

        buf = p;

    }

    ctx = log->data;

    p = ngx_snprintf(buf, len, ", client: %V", &ctx->connection->addr_text);

    len -= p - buf;

    r = ctx->request;

    if (r) {

        return r->log_handler(r, ctx->current_request, p, len);

    } else {

        p = ngx_snprintf(p, len, ", server: %V",

                         &ctx->connection->listening->addr_text);

    }

    return p;

}

static u_char *

‌ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,

    u_char *buf, size_t len)

{

    char                      *uri_separator;

    u_char                    *p;

    ngx_http_upstream_t       *u;

    ngx_http_core_srv_conf_t  *cscf;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name);

    len -= p - buf;

    buf = p;

    if (r->request_line.data == NULL && r->request_start) {

        for (p = r->request_start; p < r->header_in->last; p++) {

            if (*p == CR || *p == LF) {

                break;

            }

        }

        r->request_line.len = p - r->request_start;

        r->request_line.data = r->request_start;

    }

    if (r->request_line.len) {

        p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line);

        len -= p - buf;

        buf = p;

    }

    if (r != sr) {

        p = ngx_snprintf(buf, len, ", subrequest: \"%V\"", &sr->uri);

        len -= p - buf;

        buf = p;

    }

    u = sr->upstream;

    if (u && u->peer.name) {

        uri_separator = "";

#if (NGX_HAVE_UNIX_DOMAIN)

        if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) {

            uri_separator = ":";

        }

#endif

        p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"",

                         &u->schema, u->peer.name,

                         uri_separator, &u->uri);

        len -= p - buf;

        buf = p;

    }

    if (r->headers_in.host) {

        p = ngx_snprintf(buf, len, ", host: \"%V\"",

                         &r->headers_in.host->value);

        len -= p - buf;

        buf = p;

    }

    if (r->headers_in.referer) {

        p = ngx_snprintf(buf, len, ", referrer: \"%V\"",

                         &r->headers_in.referer->value);

        buf = p;

    }

    return buf;

}

src/http/modules/ngx_http_ssl_module.h - nginx-1.7.9

Data types defined

ngx_http_ssl_srv_conf_t

Macros defined

_NGX_HTTP_SSL_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_SSL_H_INCLUDED_

‌#define _NGX_HTTP_SSL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_flag_t                      enable;

    ngx_ssl_t                       ssl;

    ngx_flag_t                      prefer_server_ciphers;

    ngx_uint_t                      protocols;

    ngx_uint_t                      verify;

    ngx_uint_t                      verify_depth;

    size_t                          buffer_size;

    ssize_t                         builtin_session_cache;

    time_t                          session_timeout;

    ngx_str_t                       certificate;

    ngx_str_t                       certificate_key;

    ngx_str_t                       dhparam;

    ngx_str_t                       ecdh_curve;

    ngx_str_t                       client_certificate;

    ngx_str_t                       trusted_certificate;

    ngx_str_t                       crl;

    ngx_str_t                       ciphers;

    ngx_array_t                    *passwords;

    ngx_shm_zone_t                 *shm_zone;

    ngx_flag_t                      session_tickets;

    ngx_array_t                    *session_ticket_keys;

    ngx_flag_t                      stapling;

    ngx_flag_t                      stapling_verify;

    ngx_str_t                       stapling_file;

    ngx_str_t                       stapling_responder;

    u_char                         *file;

    ngx_uint_t                      line;

‌} ngx_http_ssl_srv_conf_t;

extern ngx_module_t  ngx_http_ssl_module;

#endif /* _NGX_HTTP_SSL_H_INCLUDED_ */

src/event/ngx_event_openssl.h - nginx-1.7.9

Data types defined

ngx_ssl_connection_t

ngx_ssl_sess_id_s

ngx_ssl_sess_id_t

ngx_ssl_session_cache_t

ngx_ssl_session_ticket_key_t

ngx_ssl_t

Macros defined

NGX_SSL_BUFFER

NGX_SSL_BUFSIZE

NGX_SSL_CLIENT

NGX_SSL_DFLT_BUILTIN_SCACHE

NGX_SSL_MAX_SESSION_SIZE

NGX_SSL_NAME

NGX_SSL_NONE_SCACHE

NGX_SSL_NO_BUILTIN_SCACHE

NGX_SSL_NO_SCACHE

NGX_SSL_SSLv2

NGX_SSL_SSLv3

NGX_SSL_TLSv1

NGX_SSL_TLSv1_1

NGX_SSL_TLSv1_2

_NGX_EVENT_OPENSSL_H_INCLUDED_

ngx_ssl_conn_t

ngx_ssl_free_session

ngx_ssl_get_connection

ngx_ssl_get_server_conf

ngx_ssl_get_session

ngx_ssl_session_t

ngx_ssl_verify_error_optional

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_OPENSSL_H_INCLUDED_

‌#define _NGX_EVENT_OPENSSL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <openssl/ssl.h>

#include <openssl/err.h>

#include <openssl/bn.h>

#include <openssl/conf.h>

#include <openssl/crypto.h>

#include <openssl/dh.h>

#ifndef OPENSSL_NO_ENGINE

#include <openssl/engine.h>

#endif

#include <openssl/evp.h>

#ifndef OPENSSL_NO_OCSP

#include <openssl/ocsp.h>

#endif

#include <openssl/rand.h>

#include <openssl/rsa.h>

#include <openssl/x509.h>

#include <openssl/x509v3.h>

‌#define NGX_SSL_NAME     "OpenSSL"

‌#define ngx_ssl_session_t       SSL_SESSION

‌#define ngx_ssl_conn_t          SSL

typedef struct {

    SSL_CTX                    *ctx;

    ngx_log_t                  *log;

    size_t                      buffer_size;

‌} ngx_ssl_t;

typedef struct {

    ngx_ssl_conn_t             *connection;

    ngx_int_t                   last;

    ngx_buf_t                  *buf;

    size_t                      buffer_size;

    ngx_connection_handler_pt   handler;

    ngx_event_handler_pt        saved_read_handler;

    ngx_event_handler_pt        saved_write_handler;

    unsigned                    handshaked:1;

    unsigned                    renegotiation:1;

    unsigned                    buffer:1;

    unsigned                    no_wait_shutdown:1;

    unsigned                    no_send_shutdown:1;

    unsigned                    handshake_buffer_set:1;

‌} ngx_ssl_connection_t;

‌#define NGX_SSL_NO_SCACHE            -2

‌#define NGX_SSL_NONE_SCACHE          -3

‌#define NGX_SSL_NO_BUILTIN_SCACHE    -4

‌#define NGX_SSL_DFLT_BUILTIN_SCACHE  -5

‌#define NGX_SSL_MAX_SESSION_SIZE  4096

‌typedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;

‌struct ngx_ssl_sess_id_s {

    ngx_rbtree_node_t           node;

    u_char                     *id;

    size_t                      len;

    u_char                     *session;

    ngx_queue_t                 queue;

    time_t                      expire;

#if (NGX_PTR_SIZE == 8)

    void                       *stub;

    u_char                      sess_id[32];

#endif

};

typedef struct {

    ngx_rbtree_t                session_rbtree;

    ngx_rbtree_node_t           sentinel;

    ngx_queue_t                 expire_queue;

‌} ngx_ssl_session_cache_t;

#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB

typedef struct {

    u_char                      name[16];

    u_char                      aes_key[16];

    u_char                      hmac_key[16];

‌} ngx_ssl_session_ticket_key_t;

#endif

‌#define NGX_SSL_SSLv2    0x0002

‌#define NGX_SSL_SSLv3    0x0004

‌#define NGX_SSL_TLSv1    0x0008

‌#define NGX_SSL_TLSv1_1  0x0010

‌#define NGX_SSL_TLSv1_2  0x0020

‌#define NGX_SSL_BUFFER   1

‌#define NGX_SSL_CLIENT   2

‌#define NGX_SSL_BUFSIZE  16384

ngx_int_t ngx_ssl_init(ngx_log_t *log);

ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);

ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);

ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *cert, ngx_int_t depth);

ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *cert, ngx_int_t depth);

ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);

ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);

ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);

RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,

    int key_length);

ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);

ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);

ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);

ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,

    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);

ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_array_t *paths);

ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);

ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,

    ngx_uint_t flags);

void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);

ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);

‌#define ngx_ssl_get_session(c)      SSL_get1_session(c->ssl->connection)

‌#define ngx_ssl_free_session        SSL_SESSION_free

‌#define ngx_ssl_get_connection(ssl_conn)                                      \

    SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)

‌#define ngx_ssl_get_server_conf(ssl_ctx)                                      \

    SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)

‌#define ngx_ssl_verify_error_optional(n)                                      \

    (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT                              \

     || n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN                             \

     || n == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY                     \

     || n == X509_V_ERR_CERT_UNTRUSTED                                        \

     || n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)

ngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name);

ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,

    ngx_str_t *s);

ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);

ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);

ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);

ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);

ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

void ngx_ssl_free_buffer(ngx_connection_t *c);

ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);

void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,

    char *fmt, ...);

void ngx_ssl_cleanup_ctx(void *data);

extern int  ngx_ssl_connection_index;

extern int  ngx_ssl_server_conf_index;

extern int  ngx_ssl_session_cache_index;

extern int  ngx_ssl_session_ticket_keys_index;

extern int  ngx_ssl_certificate_index;

extern int  ngx_ssl_stapling_index;

#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */

src/event/ngx_event_openssl.c - nginx-1.7.9

Global variables defined

ngx_openssl_commands

ngx_openssl_module

ngx_openssl_module_ctx

ngx_ssl_certificate_index

ngx_ssl_connection_index

ngx_ssl_server_conf_index

ngx_ssl_session_cache_index

ngx_ssl_session_ticket_keys_index

ngx_ssl_stapling_index

Data types defined

ngx_openssl_conf_t

Functions defined

ngx_openssl_create_conf

ngx_openssl_engine

ngx_openssl_exit

ngx_ssl_certificate

ngx_ssl_check_host

ngx_ssl_check_name

ngx_ssl_cleanup_ctx

ngx_ssl_clear_error

ngx_ssl_client_certificate

ngx_ssl_connection_error

ngx_ssl_create

ngx_ssl_create_connection

ngx_ssl_crl

ngx_ssl_dhparam

ngx_ssl_ecdh_curve

ngx_ssl_error

ngx_ssl_expire_sessions

ngx_ssl_free_buffer

ngx_ssl_get_cached_session

ngx_ssl_get_certificate

ngx_ssl_get_cipher_name

ngx_ssl_get_client_verify

ngx_ssl_get_fingerprint

ngx_ssl_get_issuer_dn

ngx_ssl_get_protocol

ngx_ssl_get_raw_certificate

ngx_ssl_get_serial_number

ngx_ssl_get_server_name

ngx_ssl_get_session_id

ngx_ssl_get_session_reused

ngx_ssl_get_subject_dn

ngx_ssl_handle_recv

ngx_ssl_handshake

ngx_ssl_handshake_handler

ngx_ssl_info_callback

ngx_ssl_init

ngx_ssl_new_session

ngx_ssl_password_callback

ngx_ssl_passwords_cleanup

ngx_ssl_read_handler

ngx_ssl_read_password_file

ngx_ssl_recv

ngx_ssl_recv_chain

ngx_ssl_remove_cached_session

ngx_ssl_remove_session

ngx_ssl_rsa512_key_callback

ngx_ssl_send_chain

ngx_ssl_session_cache

ngx_ssl_session_cache_init

ngx_ssl_session_id_context

ngx_ssl_session_rbtree_insert_value

ngx_ssl_session_ticket_key_callback

ngx_ssl_session_ticket_keys

ngx_ssl_session_ticket_keys

ngx_ssl_set_session

ngx_ssl_shutdown

ngx_ssl_shutdown_handler

ngx_ssl_trusted_certificate

ngx_ssl_verify_callback

ngx_ssl_write

ngx_ssl_write_handler

Macros defined

NGX_SSL_PASSWORD_BUFFER_SIZE

ngx_ssl_session_ticket_md

ngx_ssl_session_ticket_md

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define NGX_SSL_PASSWORD_BUFFER_SIZE  4096

typedef struct {

    ngx_uint_t  engine;   /* unsigned  engine:1; */

‌} ngx_openssl_conf_t;

static int ngx_ssl_password_callback(char *buf, int size, int rwflag,

    void *userdata);

static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);

static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,

    int ret);

static void ngx_ssl_passwords_cleanup(void *data);

static void ngx_ssl_handshake_handler(ngx_event_t *ev);

static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);

static void ngx_ssl_write_handler(ngx_event_t *wev);

static void ngx_ssl_read_handler(ngx_event_t *rev);

static void ngx_ssl_shutdown_handler(ngx_event_t *ev);

static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,

    ngx_err_t err, char *text);

static void ngx_ssl_clear_error(ngx_log_t *log);

static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,

    ngx_str_t *sess_ctx);

ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);

static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,

    ngx_ssl_session_t *sess);

static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,

    u_char *id, int len, int *copy);

static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);

static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,

    ngx_slab_pool_t *shpool, ngx_uint_t n);

static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB

static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,

    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,

    HMAC_CTX *hctx, int enc);

#endif

#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)

static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);

#endif

static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);

static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static void ngx_openssl_exit(ngx_cycle_t *cycle);

‌static ngx_command_t  ngx_openssl_commands[] = {

    { ngx_string("ssl_engine"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_openssl_engine,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_core_module_t  ngx_openssl_module_ctx = {

    ngx_string("openssl"),

    ngx_openssl_create_conf,

    NULL

};

‌ngx_module_t  ngx_openssl_module = {

    NGX_MODULE_V1,

    &ngx_openssl_module_ctx,               /* module context */

    ngx_openssl_commands,                  /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    ngx_openssl_exit,                      /* exit master */

    NGX_MODULE_V1_PADDING

};

‌int  ngx_ssl_connection_index;

‌int  ngx_ssl_server_conf_index;

‌int  ngx_ssl_session_cache_index;

‌int  ngx_ssl_session_ticket_keys_index;

‌int  ngx_ssl_certificate_index;

‌int  ngx_ssl_stapling_index;

ngx_int_t

‌ngx_ssl_init(ngx_log_t *log)

{

#ifndef OPENSSL_IS_BORINGSSL

    OPENSSL_config(NULL);

#endif

    SSL_library_init();

    SSL_load_error_strings();

    OpenSSL_add_all_algorithms();

#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

#ifndef SSL_OP_NO_COMPRESSION

    {

    /*

     * Disable gzip compression in OpenSSL prior to 1.0.0 version,

     * this saves about 522K per connection.

     */

    int                  n;

    STACK_OF(SSL_COMP)  *ssl_comp_methods;

    ssl_comp_methods = SSL_COMP_get_compression_methods();

    n = sk_SSL_COMP_num(ssl_comp_methods);

    while (n--) {

        (void) sk_SSL_COMP_pop(ssl_comp_methods);

    }

    }

#endif

#endif

    ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);

    if (ngx_ssl_connection_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,

                                                         NULL);

    if (ngx_ssl_server_conf_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0,

                      "SSL_CTX_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,

                                                           NULL);

    if (ngx_ssl_session_cache_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0,

                      "SSL_CTX_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    ngx_ssl_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,

                                                                 NULL, NULL);

    if (ngx_ssl_session_ticket_keys_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0,

                      "SSL_CTX_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,

                                                         NULL);

    if (ngx_ssl_certificate_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0,

                      "SSL_CTX_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,

                                                      NULL);

    if (ngx_ssl_stapling_index == -1) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0,

                      "SSL_CTX_get_ex_new_index() failed");

        return NGX_ERROR;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)

{

    ssl->ctx = SSL_CTX_new(SSLv23_method());

    if (ssl->ctx == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed");

        return NGX_ERROR;

    }

    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_set_ex_data() failed");

        return NGX_ERROR;

    }

    ssl->buffer_size = NGX_SSL_BUFSIZE;

    /* client side options */

#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);

#endif

#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);

#endif

    /* server side options */

#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);

#endif

#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER

    SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);

#endif

#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING

    /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */

    SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);

#endif

#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);

#endif

#ifdef SSL_OP_TLS_D5_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);

#endif

#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG

    SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);

#endif

#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS

    SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);

#endif

    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);

    if (!(protocols & NGX_SSL_SSLv2)) {

        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);

    }

    if (!(protocols & NGX_SSL_SSLv3)) {

        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);

    }

    if (!(protocols & NGX_SSL_TLSv1)) {

        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);

    }

#ifdef SSL_OP_NO_TLSv1_1

    if (!(protocols & NGX_SSL_TLSv1_1)) {

        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);

    }

#endif

#ifdef SSL_OP_NO_TLSv1_2

    if (!(protocols & NGX_SSL_TLSv1_2)) {

        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);

    }

#endif

#ifdef SSL_OP_NO_COMPRESSION

    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);

#endif

#ifdef SSL_MODE_RELEASE_BUFFERS

    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);

#endif

    SSL_CTX_set_read_ahead(ssl->ctx, 1);

    SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,

    ngx_str_t *key, ngx_array_t *passwords)

{

    BIO         *bio;

    X509        *x509;

    u_long       n;

    ngx_str_t   *pwd;

    ngx_uint_t   tries;

    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    /*

     * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't

     * allow to access certificate later from SSL_CTX, so we reimplement

     * it here

     */

    bio = BIO_new_file((char *) cert->data, "r");

    if (bio == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "BIO_new_file(\"%s\") failed", cert->data);

        return NGX_ERROR;

    }

    x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);

    if (x509 == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);

        BIO_free(bio);

        return NGX_ERROR;

    }

    if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_use_certificate(\"%s\") failed", cert->data);

        X509_free(x509);

        BIO_free(bio);

        return NGX_ERROR;

    }

    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_set_ex_data() failed");

        X509_free(x509);

        BIO_free(bio);

        return NGX_ERROR;

    }

    X509_free(x509);

    /* read rest of the chain */

    for ( ;; ) {

        x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);

        if (x509 == NULL) {

            n = ERR_peek_last_error();

            if (ERR_GET_LIB(n) == ERR_LIB_PEM

                && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)

            {

                /* end of file */

                ERR_clear_error();

                break;

            }

            /* some real error */

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "PEM_read_bio_X509(\"%s\") failed", cert->data);

            BIO_free(bio);

            return NGX_ERROR;

        }

        if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "SSL_CTX_add_extra_chain_cert(\"%s\") failed",

                          cert->data);

            X509_free(x509);

            BIO_free(bio);

            return NGX_ERROR;

        }

    }

    BIO_free(bio);

    if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {

#ifndef OPENSSL_NO_ENGINE

        u_char      *p, *last;

        ENGINE      *engine;

        EVP_PKEY    *pkey;

        p = key->data + sizeof("engine:") - 1;

        last = (u_char *) ngx_strchr(p, ':');

        if (last == NULL) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid syntax in \"%V\"", key);

            return NGX_ERROR;

        }

        *last = '\0';

        engine = ENGINE_by_id((char *) p);

        if (engine == NULL) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "ENGINE_by_id(\"%s\") failed", p);

            return NGX_ERROR;

        }

        *last++ = ':';

        pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);

        if (pkey == NULL) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "ENGINE_load_private_key(\"%s\") failed", last);

            ENGINE_free(engine);

            return NGX_ERROR;

        }

        ENGINE_free(engine);

        if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "SSL_CTX_use_PrivateKey(\"%s\") failed", last);

            EVP_PKEY_free(pkey);

            return NGX_ERROR;

        }

        EVP_PKEY_free(pkey);

        return NGX_OK;

#else

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "loading \"engine:...\" certificate keys "

                           "is not supported");

        return NGX_ERROR;

#endif

    }

    if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    if (passwords) {

        tries = passwords->nelts;

        pwd = passwords->elts;

        SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);

        SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);

    } else {

        tries = 1;

#if (NGX_SUPPRESS_WARN)

        pwd = NULL;

#endif

    }

    for ( ;; ) {

        if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,

                                        SSL_FILETYPE_PEM)

            != 0)

        {

            break;

        }

        if (--tries) {

            ERR_clear_error();

            SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);

            continue;

        }

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);

        return NGX_ERROR;

    }

    SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);

    return NGX_OK;

}

static int

‌ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)

{

    ngx_str_t *pwd = userdata;

    if (rwflag) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                      "ngx_ssl_password_callback() is called for encryption");

        return 0;

    }

    if (pwd->len > (size_t) size) {

        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,

                      "password is truncated to %d bytes", size);

    } else {

        size = pwd->len;

    }

    ngx_memcpy(buf, pwd->data, size);

    return size;

}

ngx_int_t

‌ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,

    ngx_int_t depth)

{

    STACK_OF(X509_NAME)  *list;

    SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);

    SSL_CTX_set_verify_depth(ssl->ctx, depth);

    if (cert->len == 0) {

        return NGX_OK;

    }

    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_load_verify_locations(\"%s\") failed",

                      cert->data);

        return NGX_ERROR;

    }

    /*

     * SSL_CTX_load_verify_locations() may leave errors in the error queue

     * while returning success

     */

    ERR_clear_error();

    list = SSL_load_client_CA_file((char *) cert->data);

    if (list == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_load_client_CA_file(\"%s\") failed", cert->data);

        return NGX_ERROR;

    }

    /*

     * before 0.9.7h and 0.9.8 SSL_load_client_CA_file()

     * always leaved an error in the error queue

     */

    ERR_clear_error();

    SSL_CTX_set_client_CA_list(ssl->ctx, list);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,

    ngx_int_t depth)

{

    SSL_CTX_set_verify_depth(ssl->ctx, depth);

    if (cert->len == 0) {

        return NGX_OK;

    }

    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_load_verify_locations(\"%s\") failed",

                      cert->data);

        return NGX_ERROR;

    }

    /*

     * SSL_CTX_load_verify_locations() may leave errors in the error queue

     * while returning success

     */

    ERR_clear_error();

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)

{

    X509_STORE   *store;

    X509_LOOKUP  *lookup;

    if (crl->len == 0) {

        return NGX_OK;

    }

    if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    store = SSL_CTX_get_cert_store(ssl->ctx);

    if (store == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_get_cert_store() failed");

        return NGX_ERROR;

    }

    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());

    if (lookup == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_STORE_add_lookup() failed");

        return NGX_ERROR;

    }

    if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_LOOKUP_load_file(\"%s\") failed", crl->data);

        return NGX_ERROR;

    }

    X509_STORE_set_flags(store,

                         X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);

    return NGX_OK;

}

static int

‌ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)

{

#if (NGX_DEBUG)

    char              *subject, *issuer;

    int                err, depth;

    X509              *cert;

    X509_NAME         *sname, *iname;

    ngx_connection_t  *c;

    ngx_ssl_conn_t    *ssl_conn;

    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,

                                          SSL_get_ex_data_X509_STORE_CTX_idx());

    c = ngx_ssl_get_connection(ssl_conn);

    cert = X509_STORE_CTX_get_current_cert(x509_store);

    err = X509_STORE_CTX_get_error(x509_store);

    depth = X509_STORE_CTX_get_error_depth(x509_store);

    sname = X509_get_subject_name(cert);

    subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";

    iname = X509_get_issuer_name(cert);

    issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";

    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "verify:%d, error:%d, depth:%d, "

                   "subject:\"%s\", issuer:\"%s\"",

                   ok, err, depth, subject, issuer);

    if (sname) {

        OPENSSL_free(subject);

    }

    if (iname) {

        OPENSSL_free(issuer);

    }

#endif

    return 1;

}

static void

‌ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)

{

    BIO               *rbio, *wbio;

    ngx_connection_t  *c;

    if (where & SSL_CB_HANDSHAKE_START) {

        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);

        if (c->ssl->handshaked) {

            c->ssl->renegotiation = 1;

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation");

        }

    }

    if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {

        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);

        if (!c->ssl->handshake_buffer_set) {

            /*

             * By default OpenSSL uses 4k buffer during a handshake,

             * which is too low for long certificate chains and might

             * result in extra round-trips.

             *

             * To adjust a buffer size we detect that buffering was added

             * to write side of the connection by comparing rbio and wbio.

             * If they are different, we assume that it's due to buffering

             * added to wbio, and set buffer size.

             */

            rbio = SSL_get_rbio((ngx_ssl_conn_t *) ssl_conn);

            wbio = SSL_get_wbio((ngx_ssl_conn_t *) ssl_conn);

            if (rbio != wbio) {

                (void) BIO_set_write_buffer_size(wbio, NGX_SSL_BUFSIZE);

                c->ssl->handshake_buffer_set = 1;

            }

        }

    }

}

RSA *

‌ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,

    int key_length)

{

    static RSA  *key;

    if (key_length != 512) {

        return NULL;

    }

#ifndef OPENSSL_NO_DEPRECATED

    if (key == NULL) {

        key = RSA_generate_key(512, RSA_F4, NULL, NULL);

    }

#endif

    return key;

}

ngx_array_t *

‌ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)

{

    u_char              *p, *last, *end;

    size_t               len;

    ssize_t              n;

    ngx_fd_t             fd;

    ngx_str_t           *pwd;

    ngx_array_t         *passwords;

    ngx_pool_cleanup_t  *cln;

    u_char               buf[NGX_SSL_PASSWORD_BUFFER_SIZE];

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {

        return NULL;

    }

    cln = ngx_pool_cleanup_add(cf->temp_pool, 0);

    passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));

    if (cln == NULL || passwords == NULL) {

        return NULL;

    }

    cln->handler = ngx_ssl_passwords_cleanup;

    cln->data = passwords;

    fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

    if (fd == NGX_INVALID_FILE) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                           ngx_open_file_n " \"%s\" failed", file->data);

        return NULL;

    }

    len = 0;

    last = buf;

    do {

        n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);

        if (n == -1) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                               ngx_read_fd_n " \"%s\" failed", file->data);

            passwords = NULL;

            goto cleanup;

        }

        end = last + n;

        if (len && n == 0) {

            *end++ = LF;

        }

        p = buf;

        for ( ;; ) {

            last = ngx_strlchr(last, end, LF);

            if (last == NULL) {

                break;

            }

            len = last++ - p;

            if (len && p[len - 1] == CR) {

                len--;

            }

            if (len) {

                pwd = ngx_array_push(passwords);

                if (pwd == NULL) {

                    passwords = NULL;

                    goto cleanup;

                }

                pwd->len = len;

                pwd->data = ngx_pnalloc(cf->temp_pool, len);

                if (pwd->data == NULL) {

                    passwords->nelts--;

                    passwords = NULL;

                    goto cleanup;

                }

                ngx_memcpy(pwd->data, p, len);

            }

            p = last;

        }

        len = end - p;

        if (len == NGX_SSL_PASSWORD_BUFFER_SIZE) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "too long line in \"%s\"", file->data);

            passwords = NULL;

            goto cleanup;

        }

        ngx_memmove(buf, p, len);

        last = buf + len;

    } while (n != 0);

    if (passwords->nelts == 0) {

        pwd = ngx_array_push(passwords);

        if (pwd == NULL) {

            passwords = NULL;

            goto cleanup;

        }

        ngx_memzero(pwd, sizeof(ngx_str_t));

    }

cleanup:

    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

        ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno,

                           ngx_close_file_n " \"%s\" failed", file->data);

    }

    ngx_memzero(buf, NGX_SSL_PASSWORD_BUFFER_SIZE);

    return passwords;

}

static void

‌ngx_ssl_passwords_cleanup(void *data)

{

    ngx_array_t *passwords = data;

    ngx_str_t   *pwd;

    ngx_uint_t   i;

    pwd = passwords->elts;

    for (i = 0; i < passwords->nelts; i++) {

        ngx_memzero(pwd[i].data, pwd[i].len);

    }

}

ngx_int_t

‌ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)

{

    DH   *dh;

    BIO  *bio;

    /*

     * -----BEGIN DH PARAMETERS-----

     * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc

     * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl

     * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC

     * -----END DH PARAMETERS-----

     */

    static unsigned char dh1024_p[] = {

        0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5,

        0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B,

        0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76,

        0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,

        0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04,

        0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04,

        0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF,

        0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,

        0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E,

        0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA,

        0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B

    };

    static unsigned char dh1024_g[] = { 0x02 };

    if (file->len == 0) {

        dh = DH_new();

        if (dh == NULL) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed");

            return NGX_ERROR;

        }

        dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);

        dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);

        if (dh->p == NULL || dh->g == NULL) {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed");

            DH_free(dh);

            return NGX_ERROR;

        }

        SSL_CTX_set_tmp_dh(ssl->ctx, dh);

        DH_free(dh);

        return NGX_OK;

    }

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    bio = BIO_new_file((char *) file->data, "r");

    if (bio == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "BIO_new_file(\"%s\") failed", file->data);

        return NGX_ERROR;

    }

    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);

    if (dh == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "PEM_read_bio_DHparams(\"%s\") failed", file->data);

        BIO_free(bio);

        return NGX_ERROR;

    }

    SSL_CTX_set_tmp_dh(ssl->ctx, dh);

    DH_free(dh);

    BIO_free(bio);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)

{

#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

#ifndef OPENSSL_NO_ECDH

    int      nid;

    EC_KEY  *ecdh;

    /*

     * Elliptic-Curve Diffie-Hellman parameters are either "named curves"

     * from RFC 4492 section 5.1.1, or explicitly described curves over

     * binary fields. OpenSSL only supports the "named curves", which provide

     * maximum interoperability.

     */

    nid = OBJ_sn2nid((const char *) name->data);

    if (nid == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "Unknown curve name \"%s\"", name->data);

        return NGX_ERROR;

    }

    ecdh = EC_KEY_new_by_curve_name(nid);

    if (ecdh == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "Unable to create curve \"%s\"", name->data);

        return NGX_ERROR;

    }

    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);

    SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);

    EC_KEY_free(ecdh);

#endif

#endif

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)

{

    ngx_ssl_connection_t  *sc;

    sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));

    if (sc == NULL) {

        return NGX_ERROR;

    }

    sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);

    sc->buffer_size = ssl->buffer_size;

    sc->connection = SSL_new(ssl->ctx);

    if (sc->connection == NULL) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");

        return NGX_ERROR;

    }

    if (SSL_set_fd(sc->connection, c->fd) == 0) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");

        return NGX_ERROR;

    }

    if (flags & NGX_SSL_CLIENT) {

        SSL_set_connect_state(sc->connection);

    } else {

        SSL_set_accept_state(sc->connection);

    }

    if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");

        return NGX_ERROR;

    }

    c->ssl = sc;

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)

{

    if (session) {

        if (SSL_set_session(c->ssl->connection, session) == 0) {

            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_session() failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_handshake(ngx_connection_t *c)

{

    int        n, sslerr;

    ngx_err_t  err;

    ngx_ssl_clear_error(c->log);

    n = SSL_do_handshake(c->ssl->connection);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);

    if (n == 1) {

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            return NGX_ERROR;

        }

#if (NGX_DEBUG)

        {

        char         buf[129], *s, *d;

#if OPENSSL_VERSION_NUMBER >= 0x10000000L

        const

#endif

        SSL_CIPHER  *cipher;

        cipher = SSL_get_current_cipher(c->ssl->connection);

        if (cipher) {

            SSL_CIPHER_description(cipher, &buf[1], 128);

            for (s = &buf[1], d = buf; *s; s++) {

                if (*s == ' ' && *d == ' ') {

                    continue;

                }

                if (*s == LF || *s == CR) {

                    continue;

                }

                *++d = *s;

            }

            if (*d != ' ') {

                d++;

            }

            *d = '\0';

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "SSL: %s, cipher: \"%s\"",

                           SSL_get_version(c->ssl->connection), &buf[1]);

            if (SSL_session_reused(c->ssl->connection)) {

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                               "SSL reused session");

            }

        } else {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "SSL no shared ciphers");

        }

        }

#endif

        c->ssl->handshaked = 1;

        c->recv = ngx_ssl_recv;

        c->send = ngx_ssl_write;

        c->recv_chain = ngx_ssl_recv_chain;

        c->send_chain = ngx_ssl_send_chain;

#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS

        /* initial handshake done, disable renegotiation (CVE-2009-3555) */

        if (c->ssl->connection->s3) {

            c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;

        }

#endif

        return NGX_OK;

    }

    sslerr = SSL_get_error(c->ssl->connection, n);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);

    if (sslerr == SSL_ERROR_WANT_READ) {

        c->read->ready = 0;

        c->read->handler = ngx_ssl_handshake_handler;

        c->write->handler = ngx_ssl_handshake_handler;

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        return NGX_AGAIN;

    }

    if (sslerr == SSL_ERROR_WANT_WRITE) {

        c->write->ready = 0;

        c->read->handler = ngx_ssl_handshake_handler;

        c->write->handler = ngx_ssl_handshake_handler;

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        return NGX_AGAIN;

    }

    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;

    c->ssl->no_wait_shutdown = 1;

    c->ssl->no_send_shutdown = 1;

    c->read->eof = 1;

    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {

        ngx_connection_error(c, err,

                             "peer closed connection in SSL handshake");

        return NGX_ERROR;

    }

    c->read->error = 1;

    ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");

    return NGX_ERROR;

}

static void

‌ngx_ssl_handshake_handler(ngx_event_t *ev)

{

    ngx_connection_t  *c;

    c = ev->data;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "SSL handshake handler: %d", ev->write);

    if (ev->timedout) {

        c->ssl->handler(c);

        return;

    }

    if (ngx_ssl_handshake(c) == NGX_AGAIN) {

        return;

    }

    c->ssl->handler(c);

}

ssize_t

‌ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)

{

    u_char     *last;

    ssize_t     n, bytes, size;

    ngx_buf_t  *b;

    bytes = 0;

    b = cl->buf;

    last = b->last;

    for ( ;; ) {

        size = b->end - last;

        if (limit) {

            if (bytes >= limit) {

                return bytes;

            }

            if (bytes + size > limit) {

                size = (ssize_t) (limit - bytes);

            }

        }

        n = ngx_ssl_recv(c, last, size);

        if (n > 0) {

            last += n;

            bytes += n;

            if (last == b->end) {

                cl = cl->next;

                if (cl == NULL) {

                    return bytes;

                }

                b = cl->buf;

                last = b->last;

            }

            continue;

        }

        if (bytes) {

            if (n == 0 || n == NGX_ERROR) {

                c->read->ready = 1;

            }

            return bytes;

        }

        return n;

    }

}

ssize_t

‌ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    int  n, bytes;

    if (c->ssl->last == NGX_ERROR) {

        c->read->error = 1;

        return NGX_ERROR;

    }

    if (c->ssl->last == NGX_DONE) {

        c->read->ready = 0;

        c->read->eof = 1;

        return 0;

    }

    bytes = 0;

    ngx_ssl_clear_error(c->log);

    /*

     * SSL_read() may return data in parts, so try to read

     * until SSL_read() would return no data

     */

    for ( ;; ) {

        n = SSL_read(c->ssl->connection, buf, size);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n);

        if (n > 0) {

            bytes += n;

        }

        c->ssl->last = ngx_ssl_handle_recv(c, n);

        if (c->ssl->last == NGX_OK) {

            size -= n;

            if (size == 0) {

                c->read->ready = 1;

                return bytes;

            }

            buf += n;

            continue;

        }

        if (bytes) {

            if (c->ssl->last != NGX_AGAIN) {

                c->read->ready = 1;

            }

            return bytes;

        }

        switch (c->ssl->last) {

        case NGX_DONE:

            c->read->ready = 0;

            c->read->eof = 1;

            return 0;

        case NGX_ERROR:

            c->read->error = 1;

            /* fall through */

        case NGX_AGAIN:

            return c->ssl->last;

        }

    }

}

static ngx_int_t

‌ngx_ssl_handle_recv(ngx_connection_t *c, int n)

{

    int        sslerr;

    ngx_err_t  err;

    if (c->ssl->renegotiation) {

        /*

         * disable renegotiation (CVE-2009-3555):

         * OpenSSL (at least up to 0.9.8l) does not handle disabled

         * renegotiation gracefully, so drop connection here

         */

        ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled");

        while (ERR_peek_error()) {

            ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,

                          "ignoring stale global SSL error");

        }

        ERR_clear_error();

        c->ssl->no_wait_shutdown = 1;

        c->ssl->no_send_shutdown = 1;

        return NGX_ERROR;

    }

    if (n > 0) {

        if (c->ssl->saved_write_handler) {

            c->write->handler = c->ssl->saved_write_handler;

            c->ssl->saved_write_handler = NULL;

            c->write->ready = 1;

            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

                return NGX_ERROR;

            }

            ngx_post_event(c->write, &ngx_posted_events);

        }

        return NGX_OK;

    }

    sslerr = SSL_get_error(c->ssl->connection, n);

    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);

    if (sslerr == SSL_ERROR_WANT_READ) {

        c->read->ready = 0;

        return NGX_AGAIN;

    }

    if (sslerr == SSL_ERROR_WANT_WRITE) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "peer started SSL renegotiation");

        c->write->ready = 0;

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        /*

         * we do not set the timer because there is already the read event timer

         */

        if (c->ssl->saved_write_handler == NULL) {

            c->ssl->saved_write_handler = c->write->handler;

            c->write->handler = ngx_ssl_write_handler;

        }

        return NGX_AGAIN;

    }

    c->ssl->no_wait_shutdown = 1;

    c->ssl->no_send_shutdown = 1;

    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "peer shutdown SSL cleanly");

        return NGX_DONE;

    }

    ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");

    return NGX_ERROR;

}

static void

‌ngx_ssl_write_handler(ngx_event_t *wev)

{

    ngx_connection_t  *c;

    c = wev->data;

    c->read->handler(c->read);

}

/*

* OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer

* before the SSL_write() call to decrease a SSL overhead.

*

* Besides for protocols such as HTTP it is possible to always buffer

* the output to decrease a SSL overhead some more.

*/

ngx_chain_t *

‌ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    int          n;

    ngx_uint_t   flush;

    ssize_t      send, size;

    ngx_buf_t   *buf;

    if (!c->ssl->buffer) {

        while (in) {

            if (ngx_buf_special(in->buf)) {

                in = in->next;

                continue;

            }

            n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);

            if (n == NGX_ERROR) {

                return NGX_CHAIN_ERROR;

            }

            if (n == NGX_AGAIN) {

                return in;

            }

            in->buf->pos += n;

            c->sent += n;

            if (in->buf->pos == in->buf->last) {

                in = in->next;

            }

        }

        return in;

    }

    /* the maximum limit size is the maximum int32_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_INT32_VALUE - ngx_pagesize;

    }

    buf = c->ssl->buf;

    if (buf == NULL) {

        buf = ngx_create_temp_buf(c->pool, c->ssl->buffer_size);

        if (buf == NULL) {

            return NGX_CHAIN_ERROR;

        }

        c->ssl->buf = buf;

    }

    if (buf->start == NULL) {

        buf->start = ngx_palloc(c->pool, c->ssl->buffer_size);

        if (buf->start == NULL) {

            return NGX_CHAIN_ERROR;

        }

        buf->pos = buf->start;

        buf->last = buf->start;

        buf->end = buf->start + c->ssl->buffer_size;

    }

    send = buf->last - buf->pos;

    flush = (in == NULL) ? 1 : buf->flush;

    for ( ;; ) {

        while (in && buf->last < buf->end && send < limit) {

            if (in->buf->last_buf || in->buf->flush) {

                flush = 1;

            }

            if (ngx_buf_special(in->buf)) {

                in = in->next;

                continue;

            }

            size = in->buf->last - in->buf->pos;

            if (size > buf->end - buf->last) {

                size = buf->end - buf->last;

            }

            if (send + size > limit) {

                size = (ssize_t) (limit - send);

            }

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "SSL buf copy: %d", size);

            ngx_memcpy(buf->last, in->buf->pos, size);

            buf->last += size;

            in->buf->pos += size;

            send += size;

            if (in->buf->pos == in->buf->last) {

                in = in->next;

            }

        }

        if (!flush && send < limit && buf->last < buf->end) {

            break;

        }

        size = buf->last - buf->pos;

        if (size == 0) {

            buf->flush = 0;

            c->buffered &= ~NGX_SSL_BUFFERED;

            return in;

        }

        n = ngx_ssl_write(c, buf->pos, size);

        if (n == NGX_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        if (n == NGX_AGAIN) {

            break;

        }

        buf->pos += n;

        c->sent += n;

        if (n < size) {

            break;

        }

        flush = 0;

        buf->pos = buf->start;

        buf->last = buf->start;

        if (in == NULL || send == limit) {

            break;

        }

    }

    buf->flush = flush;

    if (buf->pos < buf->last) {

        c->buffered |= NGX_SSL_BUFFERED;

    } else {

        c->buffered &= ~NGX_SSL_BUFFERED;

    }

    return in;

}

ssize_t

‌ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)

{

    int        n, sslerr;

    ngx_err_t  err;

    ngx_ssl_clear_error(c->log);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);

    n = SSL_write(c->ssl->connection, data, size);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);

    if (n > 0) {

        if (c->ssl->saved_read_handler) {

            c->read->handler = c->ssl->saved_read_handler;

            c->ssl->saved_read_handler = NULL;

            c->read->ready = 1;

            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

                return NGX_ERROR;

            }

            ngx_post_event(c->read, &ngx_posted_events);

        }

        return n;

    }

    sslerr = SSL_get_error(c->ssl->connection, n);

    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);

    if (sslerr == SSL_ERROR_WANT_WRITE) {

        c->write->ready = 0;

        return NGX_AGAIN;

    }

    if (sslerr == SSL_ERROR_WANT_READ) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "peer started SSL renegotiation");

        c->read->ready = 0;

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        /*

         * we do not set the timer because there is already

         * the write event timer

         */

        if (c->ssl->saved_read_handler == NULL) {

            c->ssl->saved_read_handler = c->read->handler;

            c->read->handler = ngx_ssl_read_handler;

        }

        return NGX_AGAIN;

    }

    c->ssl->no_wait_shutdown = 1;

    c->ssl->no_send_shutdown = 1;

    c->write->error = 1;

    ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");

    return NGX_ERROR;

}

static void

‌ngx_ssl_read_handler(ngx_event_t *rev)

{

    ngx_connection_t  *c;

    c = rev->data;

    c->write->handler(c->write);

}

void

‌ngx_ssl_free_buffer(ngx_connection_t *c)

{

    if (c->ssl->buf && c->ssl->buf->start) {

        if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {

            c->ssl->buf->start = NULL;

        }

    }

}

ngx_int_t

‌ngx_ssl_shutdown(ngx_connection_t *c)

{

    int        n, sslerr, mode;

    ngx_err_t  err;

    if (c->timedout) {

        mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;

        SSL_set_quiet_shutdown(c->ssl->connection, 1);

    } else {

        mode = SSL_get_shutdown(c->ssl->connection);

        if (c->ssl->no_wait_shutdown) {

            mode |= SSL_RECEIVED_SHUTDOWN;

        }

        if (c->ssl->no_send_shutdown) {

            mode |= SSL_SENT_SHUTDOWN;

        }

        if (c->ssl->no_wait_shutdown && c->ssl->no_send_shutdown) {

            SSL_set_quiet_shutdown(c->ssl->connection, 1);

        }

    }

    SSL_set_shutdown(c->ssl->connection, mode);

    ngx_ssl_clear_error(c->log);

    n = SSL_shutdown(c->ssl->connection);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);

    sslerr = 0;

    /* SSL_shutdown() never returns -1, on error it returns 0 */

    if (n != 1 && ERR_peek_error()) {

        sslerr = SSL_get_error(c->ssl->connection, n);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "SSL_get_error: %d", sslerr);

    }

    if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {

        SSL_free(c->ssl->connection);

        c->ssl = NULL;

        return NGX_OK;

    }

    if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {

        c->read->handler = ngx_ssl_shutdown_handler;

        c->write->handler = ngx_ssl_shutdown_handler;

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            return NGX_ERROR;

        }

        if (sslerr == SSL_ERROR_WANT_READ) {

            ngx_add_timer(c->read, 30000);

        }

        return NGX_AGAIN;

    }

    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;

    ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");

    SSL_free(c->ssl->connection);

    c->ssl = NULL;

    return NGX_ERROR;

}

static void

‌ngx_ssl_shutdown_handler(ngx_event_t *ev)

{

    ngx_connection_t           *c;

    ngx_connection_handler_pt   handler;

    c = ev->data;

    handler = c->ssl->handler;

    if (ev->timedout) {

        c->timedout = 1;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler");

    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {

        return;

    }

    handler(c);

}

static void

‌ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,

    char *text)

{

    int         n;

    ngx_uint_t  level;

    level = NGX_LOG_CRIT;

    if (sslerr == SSL_ERROR_SYSCALL) {

        if (err == NGX_ECONNRESET

            || err == NGX_EPIPE

            || err == NGX_ENOTCONN

            || err == NGX_ETIMEDOUT

            || err == NGX_ECONNREFUSED

            || err == NGX_ENETDOWN

            || err == NGX_ENETUNREACH

            || err == NGX_EHOSTDOWN

            || err == NGX_EHOSTUNREACH)

        {

            switch (c->log_error) {

            case NGX_ERROR_IGNORE_ECONNRESET:

            case NGX_ERROR_INFO:

                level = NGX_LOG_INFO;

                break;

            case NGX_ERROR_ERR:

                level = NGX_LOG_ERR;

                break;

            default:

                break;

            }

        }

    } else if (sslerr == SSL_ERROR_SSL) {

        n = ERR_GET_REASON(ERR_peek_error());

            /* handshake failures */

        if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC                        /*  103 */

            || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG                  /*  129 */

            || n == SSL_R_DIGEST_CHECK_FAILED                        /*  149 */

            || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              /*  151 */

            || n == SSL_R_EXCESSIVE_MESSAGE_SIZE                     /*  152 */

            || n == SSL_R_LENGTH_MISMATCH                            /*  159 */

            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */

            || n == SSL_R_NO_CIPHERS_SPECIFIED                       /*  183 */

            || n == SSL_R_NO_COMPRESSION_SPECIFIED                   /*  187 */

            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */

            || n == SSL_R_RECORD_LENGTH_MISMATCH                     /*  213 */

#ifdef SSL_R_PARSE_TLSEXT

            || n == SSL_R_PARSE_TLSEXT                               /*  227 */

#endif

            || n == SSL_R_UNEXPECTED_MESSAGE                         /*  244 */

            || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */

            || n == SSL_R_UNKNOWN_ALERT_TYPE                         /*  246 */

            || n == SSL_R_UNKNOWN_PROTOCOL                           /*  252 */

            || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */

            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */

#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG

            || n == SSL_R_RENEGOTIATE_EXT_TOO_LONG                   /*  335 */

            || n == SSL_R_RENEGOTIATION_ENCODING_ERR                 /*  336 */

            || n == SSL_R_RENEGOTIATION_MISMATCH                     /*  337 */

#endif

#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED

            || n == SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED       /*  338 */

#endif

#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING

            || n == SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING           /*  345 */

#endif

#ifdef SSL_R_INAPPROPRIATE_FALLBACK

            || n == SSL_R_INAPPROPRIATE_FALLBACK                     /*  373 */

#endif

            || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */

            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE             /* 1010 */

            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC                 /* 1020 */

            || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              /* 1021 */

            || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW                /* 1022 */

            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE          /* 1030 */

            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE              /* 1040 */

            || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE                 /* 1041 */

            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE                /* 1042 */

            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE        /* 1043 */

            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED            /* 1044 */

            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED            /* 1045 */

            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN            /* 1046 */

            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER              /* 1047 */

            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA                     /* 1048 */

            || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED                  /* 1049 */

            || n == SSL_R_TLSV1_ALERT_DECODE_ERROR                   /* 1050 */

            || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR                  /* 1051 */

            || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION             /* 1060 */

            || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION               /* 1070 */

            || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY          /* 1071 */

            || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR                 /* 1080 */

            || n == SSL_R_TLSV1_ALERT_USER_CANCELLED                 /* 1090 */

            || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION)              /* 1100 */

        {

            switch (c->log_error) {

            case NGX_ERROR_IGNORE_ECONNRESET:

            case NGX_ERROR_INFO:

                level = NGX_LOG_INFO;

                break;

            case NGX_ERROR_ERR:

                level = NGX_LOG_ERR;

                break;

            default:

                break;

            }

        }

    }

    ngx_ssl_error(level, c->log, err, text);

}

static void

‌ngx_ssl_clear_error(ngx_log_t *log)

{

    while (ERR_peek_error()) {

        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error");

    }

    ERR_clear_error();

}

void ngx_cdecl

‌ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)

{

    int          flags;

    u_long       n;

    va_list      args;

    u_char      *p, *last;

    u_char       errstr[NGX_MAX_CONF_ERRSTR];

    const char  *data;

    last = errstr + NGX_MAX_CONF_ERRSTR;

    va_start(args, fmt);

    p = ngx_vslprintf(errstr, last - 1, fmt, args);

    va_end(args);

    p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);

    for ( ;; ) {

        n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);

        if (n == 0) {

            break;

        }

        if (p >= last) {

            goto next;

        }

        *p++ = ' ';

        ERR_error_string_n(n, (char *) p, last - p);

        while (p < last && *p) {

            p++;

        }

        if (p < last && *data && (flags & ERR_TXT_STRING)) {

            *p++ = ':';

            p = ngx_cpystrn(p, (u_char *) data, last - p);

        }

    next:

        (void) ERR_get_error();

    }

    ngx_log_error(level, log, err, "%s)", errstr);

}

ngx_int_t

‌ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,

    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)

{

    long  cache_mode;

    SSL_CTX_set_timeout(ssl->ctx, (long) timeout);

    if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) {

        return NGX_ERROR;

    }

    if (builtin_session_cache == NGX_SSL_NO_SCACHE) {

        SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);

        return NGX_OK;

    }

    if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {

        /*

         * If the server explicitly says that it does not support

         * session reuse (see SSL_SESS_CACHE_OFF above), then

         * Outlook Express fails to upload a sent email to

         * the Sent Items folder on the IMAP server via a separate IMAP

         * connection in the background. Therefore we have a special

         * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)

         * where the server pretends that it supports session reuse,

         * but it does not actually store any session.

         */

        SSL_CTX_set_session_cache_mode(ssl->ctx,

                                       SSL_SESS_CACHE_SERVER

                                       |SSL_SESS_CACHE_NO_AUTO_CLEAR

                                       |SSL_SESS_CACHE_NO_INTERNAL_STORE);

        SSL_CTX_sess_set_cache_size(ssl->ctx, 1);

        return NGX_OK;

    }

    cache_mode = SSL_SESS_CACHE_SERVER;

    if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {

        cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;

    }

    SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);

    if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {

        if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {

            SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);

        }

    }

    if (shm_zone) {

        SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);

        SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);

        SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);

        if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)

            == 0)

        {

            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                          "SSL_CTX_set_ex_data() failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx)

{

    int                   n, i;

    X509                 *cert;

    X509_NAME            *name;

    EVP_MD_CTX            md;

    unsigned int          len;

    STACK_OF(X509_NAME)  *list;

    u_char                buf[EVP_MAX_MD_SIZE];

    /*

     * Session ID context is set based on the string provided,

     * the server certificate, and the client CA list.

     */

    EVP_MD_CTX_init(&md);

    if (EVP_DigestInit_ex(&md, EVP_sha1(), NULL) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "EVP_DigestInit_ex() failed");

        goto failed;

    }

    if (EVP_DigestUpdate(&md, sess_ctx->data, sess_ctx->len) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "EVP_DigestUpdate() failed");

        goto failed;

    }

    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);

    if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_digest() failed");

        goto failed;

    }

    if (EVP_DigestUpdate(&md, buf, len) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "EVP_DigestUpdate() failed");

        goto failed;

    }

    list = SSL_CTX_get_client_CA_list(ssl->ctx);

    if (list != NULL) {

        n = sk_X509_NAME_num(list);

        for (i = 0; i < n; i++) {

            name = sk_X509_NAME_value(list, i);

            if (X509_NAME_digest(name, EVP_sha1(), buf, &len) == 0) {

                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                              "X509_NAME_digest() failed");

                goto failed;

            }

            if (EVP_DigestUpdate(&md, buf, len) == 0) {

                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                              "EVP_DigestUpdate() failed");

                goto failed;

            }

        }

    }

    if (EVP_DigestFinal_ex(&md, buf, &len) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "EVP_DigestUpdate() failed");

        goto failed;

    }

    EVP_MD_CTX_cleanup(&md);

    if (SSL_CTX_set_session_id_context(ssl->ctx, buf, len) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_set_session_id_context() failed");

        return NGX_ERROR;

    }

    return NGX_OK;

failed:

    EVP_MD_CTX_cleanup(&md);

    return NGX_ERROR;

}

ngx_int_t

‌ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)

{

    size_t                    len;

    ngx_slab_pool_t          *shpool;

    ngx_ssl_session_cache_t  *cache;

    if (data) {

        shm_zone->data = data;

        return NGX_OK;

    }

    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    if (shm_zone->shm.exists) {

        shm_zone->data = shpool->data;

        return NGX_OK;

    }

    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));

    if (cache == NULL) {

        return NGX_ERROR;

    }

    shpool->data = cache;

    shm_zone->data = cache;

    ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,

                    ngx_ssl_session_rbtree_insert_value);

    ngx_queue_init(&cache->expire_queue);

    len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;

    shpool->log_ctx = ngx_slab_alloc(shpool, len);

    if (shpool->log_ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",

                &shm_zone->shm.name);

    shpool->log_nomem = 0;

    return NGX_OK;

}

/*

* The length of the session id is 16 bytes for SSLv2 sessions and

* between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.

* It seems that the typical length of the external ASN1 representation

* of a session is 118 or 119 bytes for SSLv3/TSLv1.

*

* Thus on 32-bit platforms we allocate separately an rbtree node,

* a session id, and an ASN1 representation, they take accordingly

* 64, 32, and 128 bytes.

*

* On 64-bit platforms we allocate separately an rbtree node + session_id,

* and an ASN1 representation, they take accordingly 128 and 128 bytes.

*

* OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,

* so they are outside the code locked by shared pool mutex

*/

static int

‌ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)

{

    int                       len;

    u_char                   *p, *id, *cached_sess, *session_id;

    uint32_t                  hash;

    SSL_CTX                  *ssl_ctx;

    unsigned int              session_id_length;

    ngx_shm_zone_t           *shm_zone;

    ngx_connection_t         *c;

    ngx_slab_pool_t          *shpool;

    ngx_ssl_sess_id_t        *sess_id;

    ngx_ssl_session_cache_t  *cache;

    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];

    len = i2d_SSL_SESSION(sess, NULL);

    /* do not cache too big session */

    if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {

        return 0;

    }

    p = buf;

    i2d_SSL_SESSION(sess, &p);

    c = ngx_ssl_get_connection(ssl_conn);

    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);

    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);

    cache = shm_zone->data;

    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    ngx_shmtx_lock(&shpool->mutex);

    /* drop one or two expired sessions */

    ngx_ssl_expire_sessions(cache, shpool, 1);

    cached_sess = ngx_slab_alloc_locked(shpool, len);

    if (cached_sess == NULL) {

        /* drop the oldest non-expired session and try once more */

        ngx_ssl_expire_sessions(cache, shpool, 0);

        cached_sess = ngx_slab_alloc_locked(shpool, len);

        if (cached_sess == NULL) {

            sess_id = NULL;

            goto failed;

        }

    }

    sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));

    if (sess_id == NULL) {

        /* drop the oldest non-expired session and try once more */

        ngx_ssl_expire_sessions(cache, shpool, 0);

        sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));

        if (sess_id == NULL) {

            goto failed;

        }

    }

#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

    session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);

#else

    session_id = sess->session_id;

    session_id_length = sess->session_id_length;

#endif

#if (NGX_PTR_SIZE == 8)

    id = sess_id->sess_id;

#else

    id = ngx_slab_alloc_locked(shpool, session_id_length);

    if (id == NULL) {

        /* drop the oldest non-expired session and try once more */

        ngx_ssl_expire_sessions(cache, shpool, 0);

        id = ngx_slab_alloc_locked(shpool, session_id_length);

        if (id == NULL) {

            goto failed;

        }

    }

#endif

    ngx_memcpy(cached_sess, buf, len);

    ngx_memcpy(id, session_id, session_id_length);

    hash = ngx_crc32_short(session_id, session_id_length);

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "ssl new session: %08XD:%ud:%d",

                   hash, session_id_length, len);

    sess_id->node.key = hash;

    sess_id->node.data = (u_char) session_id_length;

    sess_id->id = id;

    sess_id->len = len;

    sess_id->session = cached_sess;

    sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);

    ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);

    ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);

    ngx_shmtx_unlock(&shpool->mutex);

    return 0;

failed:

    if (cached_sess) {

        ngx_slab_free_locked(shpool, cached_sess);

    }

    if (sess_id) {

        ngx_slab_free_locked(shpool, sess_id);

    }

    ngx_shmtx_unlock(&shpool->mutex);

    ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                  "could not allocate new session%s", shpool->log_ctx);

    return 0;

}

static ngx_ssl_session_t *

‌ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,

    int *copy)

{

#if OPENSSL_VERSION_NUMBER >= 0x0090707fL

    const

#endif

    u_char                   *p;

    uint32_t                  hash;

    ngx_int_t                 rc;

    ngx_shm_zone_t           *shm_zone;

    ngx_slab_pool_t          *shpool;

    ngx_rbtree_node_t        *node, *sentinel;

    ngx_ssl_session_t        *sess;

    ngx_ssl_sess_id_t        *sess_id;

    ngx_ssl_session_cache_t  *cache;

    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];

#if (NGX_DEBUG)

    ngx_connection_t         *c;

#endif

    hash = ngx_crc32_short(id, (size_t) len);

    *copy = 0;

#if (NGX_DEBUG)

    c = ngx_ssl_get_connection(ssl_conn);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "ssl get session: %08XD:%d", hash, len);

#endif

    shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),

                                   ngx_ssl_session_cache_index);

    cache = shm_zone->data;

    sess = NULL;

    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    ngx_shmtx_lock(&shpool->mutex);

    node = cache->session_rbtree.root;

    sentinel = cache->session_rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        sess_id = (ngx_ssl_sess_id_t *) node;

        rc = ngx_memn2cmp(id, sess_id->id, (size_t) len, (size_t) node->data);

        if (rc == 0) {

            if (sess_id->expire > ngx_time()) {

                ngx_memcpy(buf, sess_id->session, sess_id->len);

                ngx_shmtx_unlock(&shpool->mutex);

                p = buf;

                sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);

                return sess;

            }

            ngx_queue_remove(&sess_id->queue);

            ngx_rbtree_delete(&cache->session_rbtree, node);

            ngx_slab_free_locked(shpool, sess_id->session);

#if (NGX_PTR_SIZE == 4)

            ngx_slab_free_locked(shpool, sess_id->id);

#endif

            ngx_slab_free_locked(shpool, sess_id);

            sess = NULL;

            goto done;

        }

        node = (rc < 0) ? node->left : node->right;

    }

done:

    ngx_shmtx_unlock(&shpool->mutex);

    return sess;

}

void

‌ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)

{

     SSL_CTX_remove_session(ssl, sess);

     ngx_ssl_remove_session(ssl, sess);

}

static void

‌ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)

{

    u_char                   *id;

    uint32_t                  hash;

    ngx_int_t                 rc;

    unsigned int              len;

    ngx_shm_zone_t           *shm_zone;

    ngx_slab_pool_t          *shpool;

    ngx_rbtree_node_t        *node, *sentinel;

    ngx_ssl_sess_id_t        *sess_id;

    ngx_ssl_session_cache_t  *cache;

    shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);

    if (shm_zone == NULL) {

        return;

    }

    cache = shm_zone->data;

#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

    id = (u_char *) SSL_SESSION_get_id(sess, &len);

#else

    id = sess->session_id;

    len = sess->session_id_length;

#endif

    hash = ngx_crc32_short(id, len);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,

                   "ssl remove session: %08XD:%ud", hash, len);

    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    ngx_shmtx_lock(&shpool->mutex);

    node = cache->session_rbtree.root;

    sentinel = cache->session_rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        sess_id = (ngx_ssl_sess_id_t *) node;

        rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);

        if (rc == 0) {

            ngx_queue_remove(&sess_id->queue);

            ngx_rbtree_delete(&cache->session_rbtree, node);

            ngx_slab_free_locked(shpool, sess_id->session);

#if (NGX_PTR_SIZE == 4)

            ngx_slab_free_locked(shpool, sess_id->id);

#endif

            ngx_slab_free_locked(shpool, sess_id);

            goto done;

        }

        node = (rc < 0) ? node->left : node->right;

    }

done:

    ngx_shmtx_unlock(&shpool->mutex);

}

static void

‌ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,

    ngx_slab_pool_t *shpool, ngx_uint_t n)

{

    time_t              now;

    ngx_queue_t        *q;

    ngx_ssl_sess_id_t  *sess_id;

    now = ngx_time();

    while (n < 3) {

        if (ngx_queue_empty(&cache->expire_queue)) {

            return;

        }

        q = ngx_queue_last(&cache->expire_queue);

        sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);

        if (n++ != 0 && sess_id->expire > now) {

            return;

        }

        ngx_queue_remove(q);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,

                       "expire session: %08Xi", sess_id->node.key);

        ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);

        ngx_slab_free_locked(shpool, sess_id->session);

#if (NGX_PTR_SIZE == 4)

        ngx_slab_free_locked(shpool, sess_id->id);

#endif

        ngx_slab_free_locked(shpool, sess_id);

    }

}

static void

‌ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t  **p;

    ngx_ssl_sess_id_t   *sess_id, *sess_id_temp;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            sess_id = (ngx_ssl_sess_id_t *) node;

            sess_id_temp = (ngx_ssl_sess_id_t *) temp;

            p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,

                              (size_t) node->data, (size_t) temp->data)

                 < 0) ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB

ngx_int_t

‌ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)

{

    u_char                         buf[48];

    ssize_t                        n;

    ngx_str_t                     *path;

    ngx_file_t                     file;

    ngx_uint_t                     i;

    ngx_array_t                   *keys;

    ngx_file_info_t                fi;

    ngx_ssl_session_ticket_key_t  *key;

    if (paths == NULL) {

        return NGX_OK;

    }

    keys = ngx_array_create(cf->pool, paths->nelts,

                            sizeof(ngx_ssl_session_ticket_key_t));

    if (keys == NULL) {

        return NGX_ERROR;

    }

    path = paths->elts;

    for (i = 0; i < paths->nelts; i++) {

        if (ngx_conf_full_name(cf->cycle, &path[i], 1) != NGX_OK) {

            return NGX_ERROR;

        }

        ngx_memzero(&file, sizeof(ngx_file_t));

        file.name = path[i];

        file.log = cf->log;

        file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0);

        if (file.fd == NGX_INVALID_FILE) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,

                               ngx_open_file_n " \"%V\" failed", &file.name);

            return NGX_ERROR;

        }

        if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {

            ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,

                               ngx_fd_info_n " \"%V\" failed", &file.name);

            goto failed;

        }

        if (ngx_file_size(&fi) != 48) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"%V\" must be 48 bytes", &file.name);

            goto failed;

        }

        n = ngx_read_file(&file, buf, 48, 0);

        if (n == NGX_ERROR) {

            ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,

                               ngx_read_file_n " \"%V\" failed", &file.name);

            goto failed;

        }

        if (n != 48) {

            ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,

                               ngx_read_file_n " \"%V\" returned only "

                               "%z bytes instead of 48", &file.name, n);

            goto failed;

        }

        key = ngx_array_push(keys);

        if (key == NULL) {

            goto failed;

        }

        ngx_memcpy(key->name, buf, 16);

        ngx_memcpy(key->aes_key, buf + 16, 16);

        ngx_memcpy(key->hmac_key, buf + 32, 16);

        if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                          ngx_close_file_n " \"%V\" failed", &file.name);

        }

    }

    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_set_ex_data() failed");

        return NGX_ERROR;

    }

    if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,

                                         ngx_ssl_session_ticket_key_callback)

        == 0)

    {

        ngx_log_error(NGX_LOG_WARN, cf->log, 0,

                      "nginx was built with Session Tickets support, however, "

                      "now it is linked dynamically to an OpenSSL library "

                      "which has no tlsext support, therefore Session Tickets "

                      "are not available");

    }

    return NGX_OK;

failed:

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                      ngx_close_file_n " \"%V\" failed", &file.name);

    }

    return NGX_ERROR;

}

#ifdef OPENSSL_NO_SHA256

‌#define ngx_ssl_session_ticket_md  EVP_sha1

#else

‌#define ngx_ssl_session_ticket_md  EVP_sha256

#endif

static int

‌ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,

    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,

    HMAC_CTX *hctx, int enc)

{

    SSL_CTX                       *ssl_ctx;

    ngx_uint_t                     i;

    ngx_array_t                   *keys;

    ngx_ssl_session_ticket_key_t  *key;

#if (NGX_DEBUG)

    u_char                         buf[32];

    ngx_connection_t              *c;

#endif

    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);

    keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);

    if (keys == NULL) {

        return -1;

    }

    key = keys->elts;

#if (NGX_DEBUG)

    c = ngx_ssl_get_connection(ssl_conn);

#endif

    if (enc == 1) {

        /* encrypt session ticket */

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "ssl session ticket encrypt, key: \"%*s\" (%s session)",

                       ngx_hex_dump(buf, key[0].name, 16) - buf, buf,

                       SSL_session_reused(ssl_conn) ? "reused" : "new");

        RAND_pseudo_bytes(iv, 16);

        EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[0].aes_key, iv);

        HMAC_Init_ex(hctx, key[0].hmac_key, 16,

                     ngx_ssl_session_ticket_md(), NULL);

        ngx_memcpy(name, key[0].name, 16);

        return 0;

    } else {

        /* decrypt session ticket */

        for (i = 0; i < keys->nelts; i++) {

            if (ngx_memcmp(name, key[i].name, 16) == 0) {

                goto found;

            }

        }

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "ssl session ticket decrypt, key: \"%*s\" not found",

                       ngx_hex_dump(buf, name, 16) - buf, buf);

        return 0;

    found:

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "ssl session ticket decrypt, key: \"%*s\"%s",

                       ngx_hex_dump(buf, key[i].name, 16) - buf, buf,

                       (i == 0) ? " (default)" : "");

        HMAC_Init_ex(hctx, key[i].hmac_key, 16,

                     ngx_ssl_session_ticket_md(), NULL);

        EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[i].aes_key, iv);

        return (i == 0) ? 1 : 2 /* renew */;

    }

}

#else

ngx_int_t

‌ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)

{

    if (paths) {

        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                      "\"ssl_session_ticket_keys\" ignored, not supported");

    }

    return NGX_OK;

}

#endif

void

‌ngx_ssl_cleanup_ctx(void *data)

{

    ngx_ssl_t  *ssl = data;

    SSL_CTX_free(ssl->ctx);

}

ngx_int_t

‌ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)

{

    X509   *cert;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_ERROR;

    }

#if (OPENSSL_VERSION_NUMBER >= 0x10002002L && !defined LIBRESSL_VERSION_NUMBER)

    /* X509_check_host() is only available in OpenSSL 1.0.2+ */

    if (name->len == 0) {

        goto failed;

    }

    if (X509_check_host(cert, (char *) name->data, name->len, 0, NULL) != 1) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "X509_check_host(): no match");

        goto failed;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "X509_check_host(): match");

    goto found;

#else

    {

    int                      n, i;

    X509_NAME               *sname;

    ASN1_STRING             *str;

    X509_NAME_ENTRY         *entry;

    GENERAL_NAME            *altname;

    STACK_OF(GENERAL_NAME)  *altnames;

    /*

     * As per RFC6125 and RFC2818, we check subjectAltName extension,

     * and if it's not present - commonName in Subject is checked.

     */

    altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);

    if (altnames) {

        n = sk_GENERAL_NAME_num(altnames);

        for (i = 0; i < n; i++) {

            altname = sk_GENERAL_NAME_value(altnames, i);

            if (altname->type != GEN_DNS) {

                continue;

            }

            str = altname->d.dNSName;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "SSL subjectAltName: \"%*s\"",

                           ASN1_STRING_length(str), ASN1_STRING_data(str));

            if (ngx_ssl_check_name(name, str) == NGX_OK) {

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                               "SSL subjectAltName: match");

                GENERAL_NAMES_free(altnames);

                goto found;

            }

        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "SSL subjectAltName: no match");

        GENERAL_NAMES_free(altnames);

        goto failed;

    }

    /*

     * If there is no subjectAltName extension, check commonName

     * in Subject.  While RFC2818 requires to only check "most specific"

     * CN, both Apache and OpenSSL check all CNs, and so do we.

     */

    sname = X509_get_subject_name(cert);

    if (sname == NULL) {

        goto failed;

    }

    i = -1;

    for ( ;; ) {

        i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);

        if (i < 0) {

            break;

        }

        entry = X509_NAME_get_entry(sname, i);

        str = X509_NAME_ENTRY_get_data(entry);

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "SSL commonName: \"%*s\"",

                       ASN1_STRING_length(str), ASN1_STRING_data(str));

        if (ngx_ssl_check_name(name, str) == NGX_OK) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "SSL commonName: match");

            goto found;

        }

    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "SSL commonName: no match");

    }

#endif

failed:

    X509_free(cert);

    return NGX_ERROR;

found:

    X509_free(cert);

    return NGX_OK;

}

#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)

static ngx_int_t

‌ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern)

{

    u_char  *s, *p, *end;

    size_t   slen, plen;

    s = name->data;

    slen = name->len;

    p = ASN1_STRING_data(pattern);

    plen = ASN1_STRING_length(pattern);

    if (slen == plen && ngx_strncasecmp(s, p, plen) == 0) {

        return NGX_OK;

    }

    if (plen > 2 && p[0] == '*' && p[1] == '.') {

        plen -= 1;

        p += 1;

        end = s + slen;

        s = ngx_strlchr(s, end, '.');

        if (s == NULL) {

            return NGX_ERROR;

        }

        slen = end - s;

        if (plen == slen && ngx_strncasecmp(s, p, plen) == 0) {

            return NGX_OK;

        }

    }

    return NGX_ERROR;

}

#endif

ngx_int_t

‌ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    s->data = (u_char *) SSL_get_version(c->ssl->connection);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    s->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    u_char        *buf;

    SSL_SESSION   *sess;

    unsigned int   len;

    sess = SSL_get0_session(c->ssl->connection);

    if (sess == NULL) {

        s->len = 0;

        return NGX_OK;

    }

#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

    buf = (u_char *) SSL_SESSION_get_id(sess, &len);

#else

    buf = sess->session_id;

    len = sess->session_id_length;

#endif

    s->len = 2 * len;

    s->data = ngx_pnalloc(pool, 2 * len);

    if (s->data == NULL) {

        return NGX_ERROR;

    }

    ngx_hex_dump(s->data, buf, len);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    if (SSL_session_reused(c->ssl->connection)) {

        ngx_str_set(s, "r");

    } else {

        ngx_str_set(s, ".");

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

    const char  *servername;

    servername = SSL_get_servername(c->ssl->connection,

                                    TLSEXT_NAMETYPE_host_name);

    if (servername) {

        s->data = (u_char *) servername;

        s->len = ngx_strlen(servername);

        return NGX_OK;

    }

#endif

    s->len = 0;

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    size_t   len;

    BIO     *bio;

    X509    *cert;

    s->len = 0;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_OK;

    }

    bio = BIO_new(BIO_s_mem());

    if (bio == NULL) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");

        X509_free(cert);

        return NGX_ERROR;

    }

    if (PEM_write_bio_X509(bio, cert) == 0) {

        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed");

        goto failed;

    }

    len = BIO_pending(bio);

    s->len = len;

    s->data = ngx_pnalloc(pool, len);

    if (s->data == NULL) {

        goto failed;

    }

    BIO_read(bio, s->data, len);

    BIO_free(bio);

    X509_free(cert);

    return NGX_OK;

failed:

    BIO_free(bio);

    X509_free(cert);

    return NGX_ERROR;

}

ngx_int_t

‌ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    u_char      *p;

    size_t       len;

    ngx_uint_t   i;

    ngx_str_t    cert;

    if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {

        return NGX_ERROR;

    }

    if (cert.len == 0) {

        s->len = 0;

        return NGX_OK;

    }

    len = cert.len - 1;

    for (i = 0; i < cert.len - 1; i++) {

        if (cert.data[i] == LF) {

            len++;

        }

    }

    s->len = len;

    s->data = ngx_pnalloc(pool, len);

    if (s->data == NULL) {

        return NGX_ERROR;

    }

    p = s->data;

    for (i = 0; i < cert.len - 1; i++) {

        *p++ = cert.data[i];

        if (cert.data[i] == LF) {

            *p++ = '\t';

        }

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    char       *p;

    size_t      len;

    X509       *cert;

    X509_NAME  *name;

    s->len = 0;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_OK;

    }

    name = X509_get_subject_name(cert);

    if (name == NULL) {

        X509_free(cert);

        return NGX_ERROR;

    }

    p = X509_NAME_oneline(name, NULL, 0);

    for (len = 0; p[len]; len++) { /* void */ }

    s->len = len;

    s->data = ngx_pnalloc(pool, len);

    if (s->data == NULL) {

        OPENSSL_free(p);

        X509_free(cert);

        return NGX_ERROR;

    }

    ngx_memcpy(s->data, p, len);

    OPENSSL_free(p);

    X509_free(cert);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    char       *p;

    size_t      len;

    X509       *cert;

    X509_NAME  *name;

    s->len = 0;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_OK;

    }

    name = X509_get_issuer_name(cert);

    if (name == NULL) {

        X509_free(cert);

        return NGX_ERROR;

    }

    p = X509_NAME_oneline(name, NULL, 0);

    for (len = 0; p[len]; len++) { /* void */ }

    s->len = len;

    s->data = ngx_pnalloc(pool, len);

    if (s->data == NULL) {

        OPENSSL_free(p);

        X509_free(cert);

        return NGX_ERROR;

    }

    ngx_memcpy(s->data, p, len);

    OPENSSL_free(p);

    X509_free(cert);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    size_t   len;

    X509    *cert;

    BIO     *bio;

    s->len = 0;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_OK;

    }

    bio = BIO_new(BIO_s_mem());

    if (bio == NULL) {

        X509_free(cert);

        return NGX_ERROR;

    }

    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert));

    len = BIO_pending(bio);

    s->len = len;

    s->data = ngx_pnalloc(pool, len);

    if (s->data == NULL) {

        BIO_free(bio);

        X509_free(cert);

        return NGX_ERROR;

    }

    BIO_read(bio, s->data, len);

    BIO_free(bio);

    X509_free(cert);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    X509          *cert;

    unsigned int   len;

    u_char         buf[EVP_MAX_MD_SIZE];

    s->len = 0;

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert == NULL) {

        return NGX_OK;

    }

    if (!X509_digest(cert, EVP_sha1(), buf, &len)) {

        X509_free(cert);

        return NGX_ERROR;

    }

    s->len = 2 * len;

    s->data = ngx_pnalloc(pool, 2 * len);

    if (s->data == NULL) {

        X509_free(cert);

        return NGX_ERROR;

    }

    ngx_hex_dump(s->data, buf, len);

    X509_free(cert);

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)

{

    X509  *cert;

    if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {

        ngx_str_set(s, "FAILED");

        return NGX_OK;

    }

    cert = SSL_get_peer_certificate(c->ssl->connection);

    if (cert) {

        ngx_str_set(s, "SUCCESS");

    } else {

        ngx_str_set(s, "NONE");

    }

    X509_free(cert);

    return NGX_OK;

}

static void *

‌ngx_openssl_create_conf(ngx_cycle_t *cycle)

{

    ngx_openssl_conf_t  *oscf;

    oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));

    if (oscf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     oscf->engine = 0;

     */

    return oscf;

}

static char *

‌ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

#ifndef OPENSSL_NO_ENGINE

    ngx_openssl_conf_t *oscf = conf;

    ENGINE     *engine;

    ngx_str_t  *value;

    if (oscf->engine) {

        return "is duplicate";

    }

    oscf->engine = 1;

    value = cf->args->elts;

    engine = ENGINE_by_id((const char *) value[1].data);

    if (engine == NULL) {

        ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,

                      "ENGINE_by_id(\"%V\") failed", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {

        ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,

                      "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed",

                      &value[1]);

        ENGINE_free(engine);

        return NGX_CONF_ERROR;

    }

    ENGINE_free(engine);

    return NGX_CONF_OK;

#else

    return "is not supported";

#endif

}

static void

‌ngx_openssl_exit(ngx_cycle_t *cycle)

{

    EVP_cleanup();

#ifndef OPENSSL_NO_ENGINE

    ENGINE_cleanup();

#endif

}

src/core/ngx_slab.h - nginx-1.7.9

Data types defined

ngx_slab_page_s

ngx_slab_page_t

ngx_slab_pool_t

Macros defined

_NGX_SLAB_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SLAB_H_INCLUDED_

‌#define _NGX_SLAB_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌typedef struct ngx_slab_page_s  ngx_slab_page_t;

‌struct ngx_slab_page_s {

    uintptr_t         slab;

    ngx_slab_page_t  *next;

    uintptr_t         prev;

};

typedef struct {

    ngx_shmtx_sh_t    lock;

    size_t            min_size;

    size_t            min_shift;

    ngx_slab_page_t  *pages;

    ngx_slab_page_t  *last;

    ngx_slab_page_t   free;

    u_char           *start;

    u_char           *end;

    ngx_shmtx_t       mutex;

    u_char           *log_ctx;

    u_char            zero;

    unsigned          log_nomem:1;

    void             *data;

    void             *addr;

‌} ngx_slab_pool_t;

void ngx_slab_init(ngx_slab_pool_t *pool);

void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);

void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);

void *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size);

void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size);

void ngx_slab_free(ngx_slab_pool_t *pool, void *p);

void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);

#endif /* _NGX_SLAB_H_INCLUDED_ */

src/core/ngx_shmtx.h - nginx-1.7.9

Data types defined

ngx_shmtx_sh_t

ngx_shmtx_t

Macros defined

_NGX_SHMTX_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SHMTX_H_INCLUDED_

‌#define _NGX_SHMTX_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    ngx_atomic_t   lock;

#if (NGX_HAVE_POSIX_SEM)

    ngx_atomic_t   wait;

#endif

‌} ngx_shmtx_sh_t;

typedef struct {

#if (NGX_HAVE_ATOMIC_OPS)

    ngx_atomic_t  *lock;

#if (NGX_HAVE_POSIX_SEM)

    ngx_atomic_t  *wait;

    ngx_uint_t     semaphore;

    sem_t          sem;

#endif

#else

    ngx_fd_t       fd;

    u_char        *name;

#endif

    ngx_uint_t     spin;

‌} ngx_shmtx_t;

ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr,

    u_char *name);

void ngx_shmtx_destroy(ngx_shmtx_t *mtx);

ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);

void ngx_shmtx_lock(ngx_shmtx_t *mtx);

void ngx_shmtx_unlock(ngx_shmtx_t *mtx);

ngx_uint_t ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid);

#endif /* _NGX_SHMTX_H_INCLUDED_ */

src/core/ngx_shmtx.c - nginx-1.7.9

Functions defined

ngx_shmtx_create

ngx_shmtx_create

ngx_shmtx_destroy

ngx_shmtx_destroy

ngx_shmtx_force_unlock

ngx_shmtx_force_unlock

ngx_shmtx_lock

ngx_shmtx_lock

ngx_shmtx_trylock

ngx_shmtx_trylock

ngx_shmtx_unlock

ngx_shmtx_unlock

ngx_shmtx_wakeup

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_ATOMIC_OPS)

static void ngx_shmtx_wakeup(ngx_shmtx_t *mtx);

ngx_int_t

‌ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)

{

    mtx->lock = &addr->lock;

    if (mtx->spin == (ngx_uint_t) -1) {

        return NGX_OK;

    }

    mtx->spin = 2048;

#if (NGX_HAVE_POSIX_SEM)

    mtx->wait = &addr->wait;

    if (sem_init(&mtx->sem, 1, 0) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,

                      "sem_init() failed");

    } else {

        mtx->semaphore = 1;

    }

#endif

    return NGX_OK;

}

void

‌ngx_shmtx_destroy(ngx_shmtx_t *mtx)

{

#if (NGX_HAVE_POSIX_SEM)

    if (mtx->semaphore) {

        if (sem_destroy(&mtx->sem) == -1) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,

                          "sem_destroy() failed");

        }

    }

#endif

}

ngx_uint_t

‌ngx_shmtx_trylock(ngx_shmtx_t *mtx)

{

    return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));

}

void

‌ngx_shmtx_lock(ngx_shmtx_t *mtx)

{

    ngx_uint_t         i, n;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock");

    for ( ;; ) {

        if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {

            return;

        }

        if (ngx_ncpu > 1) {

            for (n = 1; n < mtx->spin; n <<= 1) {

                for (i = 0; i < n; i++) {

                    ngx_cpu_pause();

                }

                if (*mtx->lock == 0

                    && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid))

                {

                    return;

                }

            }

        }

#if (NGX_HAVE_POSIX_SEM)

        if (mtx->semaphore) {

            (void) ngx_atomic_fetch_add(mtx->wait, 1);

            if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {

                return;

            }

            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                           "shmtx wait %uA", *mtx->wait);

            while (sem_wait(&mtx->sem) == -1) {

                ngx_err_t  err;

                err = ngx_errno;

                if (err != NGX_EINTR) {

                    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,

                                  "sem_wait() failed while waiting on shmtx");

                    break;

                }

            }

            ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                           "shmtx awoke");

            continue;

        }

#endif

        ngx_sched_yield();

    }

}

void

‌ngx_shmtx_unlock(ngx_shmtx_t *mtx)

{

    if (mtx->spin != (ngx_uint_t) -1) {

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock");

    }

    if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) {

        ngx_shmtx_wakeup(mtx);

    }

}

ngx_uint_t

‌ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)

{

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                   "shmtx forced unlock");

    if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) {

        ngx_shmtx_wakeup(mtx);

        return 1;

    }

    return 0;

}

static void

‌ngx_shmtx_wakeup(ngx_shmtx_t *mtx)

{

#if (NGX_HAVE_POSIX_SEM)

    ngx_atomic_uint_t  wait;

    if (!mtx->semaphore) {

        return;

    }

    for ( ;; ) {

        wait = *mtx->wait;

        if (wait == 0) {

            return;

        }

        if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) {

            break;

        }

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                   "shmtx wake %uA", wait);

    if (sem_post(&mtx->sem) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,

                      "sem_post() failed while wake shmtx");

    }

#endif

}

#else

ngx_int_t

‌ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)

{

    if (mtx->name) {

        if (ngx_strcmp(name, mtx->name) == 0) {

            mtx->name = name;

            return NGX_OK;

        }

        ngx_shmtx_destroy(mtx);

    }

    mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,

                            NGX_FILE_DEFAULT_ACCESS);

    if (mtx->fd == NGX_INVALID_FILE) {

        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,

                      ngx_open_file_n " \"%s\" failed", name);

        return NGX_ERROR;

    }

    if (ngx_delete_file(name) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,

                      ngx_delete_file_n " \"%s\" failed", name);

    }

    mtx->name = name;

    return NGX_OK;

}

void

‌ngx_shmtx_destroy(ngx_shmtx_t *mtx)

{

    if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", mtx->name);

    }

}

ngx_uint_t

‌ngx_shmtx_trylock(ngx_shmtx_t *mtx)

{

    ngx_err_t  err;

    err = ngx_trylock_fd(mtx->fd);

    if (err == 0) {

        return 1;

    }

    if (err == NGX_EAGAIN) {

        return 0;

    }

#if __osf__ /* Tru64 UNIX */

    if (err == NGX_EACCESS) {

        return 0;

    }

#endif

    ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);

    return 0;

}

void

‌ngx_shmtx_lock(ngx_shmtx_t *mtx)

{

    ngx_err_t  err;

    err = ngx_lock_fd(mtx->fd);

    if (err == 0) {

        return;

    }

    ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);

}

void

‌ngx_shmtx_unlock(ngx_shmtx_t *mtx)

{

    ngx_err_t  err;

    err = ngx_unlock_fd(mtx->fd);

    if (err == 0) {

        return;

    }

    ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);

}

ngx_uint_t

‌ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)

{

    return 0;

}

#endif

src/core/ngx_slab.c - nginx-1.7.9

Global variables defined

ngx_slab_exact_shift

ngx_slab_exact_size

ngx_slab_max_size

Functions defined

ngx_slab_alloc

ngx_slab_alloc_locked

ngx_slab_alloc_pages

ngx_slab_calloc

ngx_slab_calloc_locked

ngx_slab_error

ngx_slab_free

ngx_slab_free_locked

ngx_slab_free_pages

ngx_slab_init

Macros defined

NGX_SLAB_BIG

NGX_SLAB_BUSY

NGX_SLAB_BUSY

NGX_SLAB_EXACT

NGX_SLAB_MAP_MASK

NGX_SLAB_MAP_MASK

NGX_SLAB_MAP_SHIFT

NGX_SLAB_MAP_SHIFT

NGX_SLAB_PAGE

NGX_SLAB_PAGE_BUSY

NGX_SLAB_PAGE_BUSY

NGX_SLAB_PAGE_FREE

NGX_SLAB_PAGE_FREE

NGX_SLAB_PAGE_MASK

NGX_SLAB_PAGE_START

NGX_SLAB_PAGE_START

NGX_SLAB_SHIFT_MASK

NGX_SLAB_SHIFT_MASK

NGX_SLAB_SMALL

ngx_slab_junk

ngx_slab_junk

ngx_slab_junk

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_SLAB_PAGE_MASK   3

‌#define NGX_SLAB_PAGE        0

‌#define NGX_SLAB_BIG         1

‌#define NGX_SLAB_EXACT       2

‌#define NGX_SLAB_SMALL       3

#if (NGX_PTR_SIZE == 4)

‌#define NGX_SLAB_PAGE_FREE   0

‌#define NGX_SLAB_PAGE_BUSY   0xffffffff

‌#define NGX_SLAB_PAGE_START  0x80000000

‌#define NGX_SLAB_SHIFT_MASK  0x0000000f

‌#define NGX_SLAB_MAP_MASK    0xffff0000

‌#define NGX_SLAB_MAP_SHIFT   16

‌#define NGX_SLAB_BUSY        0xffffffff

#else /* (NGX_PTR_SIZE == 8) */

‌#define NGX_SLAB_PAGE_FREE   0

‌#define NGX_SLAB_PAGE_BUSY   0xffffffffffffffff

‌#define NGX_SLAB_PAGE_START  0x8000000000000000

‌#define NGX_SLAB_SHIFT_MASK  0x000000000000000f

‌#define NGX_SLAB_MAP_MASK    0xffffffff00000000

‌#define NGX_SLAB_MAP_SHIFT   32

‌#define NGX_SLAB_BUSY        0xffffffffffffffff

#endif

#if (NGX_DEBUG_MALLOC)

‌#define ngx_slab_junk(p, size)     ngx_memset(p, 0xA5, size)

#elif (NGX_HAVE_DEBUG_MALLOC)

‌#define ngx_slab_junk(p, size)                                                \

    if (ngx_debug_malloc)          ngx_memset(p, 0xA5, size)

#else

‌#define ngx_slab_junk(p, size)

#endif

static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool,

    ngx_uint_t pages);

static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,

    ngx_uint_t pages);

static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,

    char *text);

‌static ngx_uint_t  ngx_slab_max_size;

‌static ngx_uint_t  ngx_slab_exact_size;

‌static ngx_uint_t  ngx_slab_exact_shift;

void

‌ngx_slab_init(ngx_slab_pool_t *pool)

{

    u_char           *p;

    size_t            size;

    ngx_int_t         m;

    ngx_uint_t        i, n, pages;

    ngx_slab_page_t  *slots;

    /* STUB */

    if (ngx_slab_max_size == 0) {

        ngx_slab_max_size = ngx_pagesize / 2;

        ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));

        for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {

            /* void */

        }

    }

    /**/

    pool->min_size = 1 << pool->min_shift;

    p = (u_char *) pool + sizeof(ngx_slab_pool_t);

    size = pool->end - p;

    ngx_slab_junk(p, size);

    slots = (ngx_slab_page_t *) p;

    n = ngx_pagesize_shift - pool->min_shift;

    for (i = 0; i < n; i++) {

        slots[i].slab = 0;

        slots[i].next = &slots[i];

        slots[i].prev = 0;

    }

    p += n * sizeof(ngx_slab_page_t);

    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));

    ngx_memzero(p, pages * sizeof(ngx_slab_page_t));

    pool->pages = (ngx_slab_page_t *) p;

    pool->free.prev = 0;

    pool->free.next = (ngx_slab_page_t *) p;

    pool->pages->slab = pages;

    pool->pages->next = &pool->free;

    pool->pages->prev = (uintptr_t) &pool->free;

    pool->start = (u_char *)

                  ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),

                                 ngx_pagesize);

    m = pages - (pool->end - pool->start) / ngx_pagesize;

    if (m > 0) {

        pages -= m;

        pool->pages->slab = pages;

    }

    pool->last = pool->pages + pages;

    pool->log_nomem = 1;

    pool->log_ctx = &pool->zero;

    pool->zero = '\0';

}

void *

‌ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)

{

    void  *p;

    ngx_shmtx_lock(&pool->mutex);

    p = ngx_slab_alloc_locked(pool, size);

    ngx_shmtx_unlock(&pool->mutex);

    return p;

}

void *

‌ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)

{

    size_t            s;

    uintptr_t         p, n, m, mask, *bitmap;

    ngx_uint_t        i, slot, shift, map;

    ngx_slab_page_t  *page, *prev, *slots;

    if (size > ngx_slab_max_size) {

        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,

                       "slab alloc: %uz", size);

        page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)

                                          + ((size % ngx_pagesize) ? 1 : 0));

        if (page) {

            p = (page - pool->pages) << ngx_pagesize_shift;

            p += (uintptr_t) pool->start;

        } else {

            p = 0;

        }

        goto done;

    }

    if (size > pool->min_size) {

        shift = 1;

        for (s = size - 1; s >>= 1; shift++) { /* void */ }

        slot = shift - pool->min_shift;

    } else {

        size = pool->min_size;

        shift = pool->min_shift;

        slot = 0;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,

                   "slab alloc: %uz slot: %ui", size, slot);

    slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));

    page = slots[slot].next;

    if (page->next != page) {

        if (shift < ngx_slab_exact_shift) {

            do {

                p = (page - pool->pages) << ngx_pagesize_shift;

                bitmap = (uintptr_t *) (pool->start + p);

                map = (1 << (ngx_pagesize_shift - shift))

                          / (sizeof(uintptr_t) * 8);

                for (n = 0; n < map; n++) {

                    if (bitmap[n] != NGX_SLAB_BUSY) {

                        for (m = 1, i = 0; m; m <<= 1, i++) {

                            if ((bitmap[n] & m)) {

                                continue;

                            }

                            bitmap[n] |= m;

                            i = ((n * sizeof(uintptr_t) * 8) << shift)

                                + (i << shift);

                            if (bitmap[n] == NGX_SLAB_BUSY) {

                                for (n = n + 1; n < map; n++) {

                                     if (bitmap[n] != NGX_SLAB_BUSY) {

                                         p = (uintptr_t) bitmap + i;

                                         goto done;

                                     }

                                }

                                prev = (ngx_slab_page_t *)

                                            (page->prev & ~NGX_SLAB_PAGE_MASK);

                                prev->next = page->next;

                                page->next->prev = page->prev;

                                page->next = NULL;

                                page->prev = NGX_SLAB_SMALL;

                            }

                            p = (uintptr_t) bitmap + i;

                            goto done;

                        }

                    }

                }

                page = page->next;

            } while (page);

        } else if (shift == ngx_slab_exact_shift) {

            do {

                if (page->slab != NGX_SLAB_BUSY) {

                    for (m = 1, i = 0; m; m <<= 1, i++) {

                        if ((page->slab & m)) {

                            continue;

                        }

                        page->slab |= m;

                        if (page->slab == NGX_SLAB_BUSY) {

                            prev = (ngx_slab_page_t *)

                                            (page->prev & ~NGX_SLAB_PAGE_MASK);

                            prev->next = page->next;

                            page->next->prev = page->prev;

                            page->next = NULL;

                            page->prev = NGX_SLAB_EXACT;

                        }

                        p = (page - pool->pages) << ngx_pagesize_shift;

                        p += i << shift;

                        p += (uintptr_t) pool->start;

                        goto done;

                    }

                }

                page = page->next;

            } while (page);

        } else { /* shift > ngx_slab_exact_shift */

            n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);

            n = 1 << n;

            n = ((uintptr_t) 1 << n) - 1;

            mask = n << NGX_SLAB_MAP_SHIFT;

            do {

                if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {

                    for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;

                         m & mask;

                         m <<= 1, i++)

                    {

                        if ((page->slab & m)) {

                            continue;

                        }

                        page->slab |= m;

                        if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {

                            prev = (ngx_slab_page_t *)

                                            (page->prev & ~NGX_SLAB_PAGE_MASK);

                            prev->next = page->next;

                            page->next->prev = page->prev;

                            page->next = NULL;

                            page->prev = NGX_SLAB_BIG;

                        }

                        p = (page - pool->pages) << ngx_pagesize_shift;

                        p += i << shift;

                        p += (uintptr_t) pool->start;

                        goto done;

                    }

                }

                page = page->next;

            } while (page);

        }

    }

    page = ngx_slab_alloc_pages(pool, 1);

    if (page) {

        if (shift < ngx_slab_exact_shift) {

            p = (page - pool->pages) << ngx_pagesize_shift;

            bitmap = (uintptr_t *) (pool->start + p);

            s = 1 << shift;

            n = (1 << (ngx_pagesize_shift - shift)) / 8 / s;

            if (n == 0) {

                n = 1;

            }

            bitmap[0] = (2 << n) - 1;

            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);

            for (i = 1; i < map; i++) {

                bitmap[i] = 0;

            }

            page->slab = shift;

            page->next = &slots[slot];

            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;

            slots[slot].next = page;

            p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;

            p += (uintptr_t) pool->start;

            goto done;

        } else if (shift == ngx_slab_exact_shift) {

            page->slab = 1;

            page->next = &slots[slot];

            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;

            slots[slot].next = page;

            p = (page - pool->pages) << ngx_pagesize_shift;

            p += (uintptr_t) pool->start;

            goto done;

        } else { /* shift > ngx_slab_exact_shift */

            page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;

            page->next = &slots[slot];

            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;

            slots[slot].next = page;

            p = (page - pool->pages) << ngx_pagesize_shift;

            p += (uintptr_t) pool->start;

            goto done;

        }

    }

    p = 0;

done:

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p);

    return (void *) p;

}

void *

‌ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size)

{

    void  *p;

    ngx_shmtx_lock(&pool->mutex);

    p = ngx_slab_calloc_locked(pool, size);

    ngx_shmtx_unlock(&pool->mutex);

    return p;

}

void *

‌ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size)

{

    void  *p;

    p = ngx_slab_alloc_locked(pool, size);

    if (p) {

        ngx_memzero(p, size);

    }

    return p;

}

void

‌ngx_slab_free(ngx_slab_pool_t *pool, void *p)

{

    ngx_shmtx_lock(&pool->mutex);

    ngx_slab_free_locked(pool, p);

    ngx_shmtx_unlock(&pool->mutex);

}

void

‌ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)

{

    size_t            size;

    uintptr_t         slab, m, *bitmap;

    ngx_uint_t        n, type, slot, shift, map;

    ngx_slab_page_t  *slots, *page;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);

    if ((u_char *) p < pool->start || (u_char *) p > pool->end) {

        ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");

        goto fail;

    }

    n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;

    page = &pool->pages[n];

    slab = page->slab;

    type = page->prev & NGX_SLAB_PAGE_MASK;

    switch (type) {

    case NGX_SLAB_SMALL:

        shift = slab & NGX_SLAB_SHIFT_MASK;

        size = 1 << shift;

        if ((uintptr_t) p & (size - 1)) {

            goto wrong_chunk;

        }

        n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;

        m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));

        n /= (sizeof(uintptr_t) * 8);

        bitmap = (uintptr_t *)

                             ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));

        if (bitmap[n] & m) {

            if (page->next == NULL) {

                slots = (ngx_slab_page_t *)

                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));

                slot = shift - pool->min_shift;

                page->next = slots[slot].next;

                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;

                page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;

            }

            bitmap[n] &= ~m;

            n = (1 << (ngx_pagesize_shift - shift)) / 8 / (1 << shift);

            if (n == 0) {

                n = 1;

            }

            if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) {

                goto done;

            }

            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);

            for (n = 1; n < map; n++) {

                if (bitmap[n]) {

                    goto done;

                }

            }

            ngx_slab_free_pages(pool, page, 1);

            goto done;

        }

        goto chunk_already_free;

    case NGX_SLAB_EXACT:

        m = (uintptr_t) 1 <<

                (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift);

        size = ngx_slab_exact_size;

        if ((uintptr_t) p & (size - 1)) {

            goto wrong_chunk;

        }

        if (slab & m) {

            if (slab == NGX_SLAB_BUSY) {

                slots = (ngx_slab_page_t *)

                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));

                slot = ngx_slab_exact_shift - pool->min_shift;

                page->next = slots[slot].next;

                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;

                page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;

            }

            page->slab &= ~m;

            if (page->slab) {

                goto done;

            }

            ngx_slab_free_pages(pool, page, 1);

            goto done;

        }

        goto chunk_already_free;

    case NGX_SLAB_BIG:

        shift = slab & NGX_SLAB_SHIFT_MASK;

        size = 1 << shift;

        if ((uintptr_t) p & (size - 1)) {

            goto wrong_chunk;

        }

        m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift)

                              + NGX_SLAB_MAP_SHIFT);

        if (slab & m) {

            if (page->next == NULL) {

                slots = (ngx_slab_page_t *)

                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));

                slot = shift - pool->min_shift;

                page->next = slots[slot].next;

                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;

                page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;

            }

            page->slab &= ~m;

            if (page->slab & NGX_SLAB_MAP_MASK) {

                goto done;

            }

            ngx_slab_free_pages(pool, page, 1);

            goto done;

        }

        goto chunk_already_free;

    case NGX_SLAB_PAGE:

        if ((uintptr_t) p & (ngx_pagesize - 1)) {

            goto wrong_chunk;

        }

        if (slab == NGX_SLAB_PAGE_FREE) {

            ngx_slab_error(pool, NGX_LOG_ALERT,

                           "ngx_slab_free(): page is already free");

            goto fail;

        }

        if (slab == NGX_SLAB_PAGE_BUSY) {

            ngx_slab_error(pool, NGX_LOG_ALERT,

                           "ngx_slab_free(): pointer to wrong page");

            goto fail;

        }

        n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;

        size = slab & ~NGX_SLAB_PAGE_START;

        ngx_slab_free_pages(pool, &pool->pages[n], size);

        ngx_slab_junk(p, size << ngx_pagesize_shift);

        return;

    }

    /* not reached */

    return;

done:

    ngx_slab_junk(p, size);

    return;

wrong_chunk:

    ngx_slab_error(pool, NGX_LOG_ALERT,

                   "ngx_slab_free(): pointer to wrong chunk");

    goto fail;

chunk_already_free:

    ngx_slab_error(pool, NGX_LOG_ALERT,

                   "ngx_slab_free(): chunk is already free");

fail:

    return;

}

static ngx_slab_page_t *

‌ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)

{

    ngx_slab_page_t  *page, *p;

    for (page = pool->free.next; page != &pool->free; page = page->next) {

        if (page->slab >= pages) {

            if (page->slab > pages) {

                page[page->slab - 1].prev = (uintptr_t) &page[pages];

                page[pages].slab = page->slab - pages;

                page[pages].next = page->next;

                page[pages].prev = page->prev;

                p = (ngx_slab_page_t *) page->prev;

                p->next = &page[pages];

                page->next->prev = (uintptr_t) &page[pages];

            } else {

                p = (ngx_slab_page_t *) page->prev;

                p->next = page->next;

                page->next->prev = page->prev;

            }

            page->slab = pages | NGX_SLAB_PAGE_START;

            page->next = NULL;

            page->prev = NGX_SLAB_PAGE;

            if (--pages == 0) {

                return page;

            }

            for (p = page + 1; pages; pages--) {

                p->slab = NGX_SLAB_PAGE_BUSY;

                p->next = NULL;

                p->prev = NGX_SLAB_PAGE;

                p++;

            }

            return page;

        }

    }

    if (pool->log_nomem) {

        ngx_slab_error(pool, NGX_LOG_CRIT,

                       "ngx_slab_alloc() failed: no memory");

    }

    return NULL;

}

static void

‌ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,

    ngx_uint_t pages)

{

    ngx_uint_t        type;

    ngx_slab_page_t  *prev, *join;

    page->slab = pages--;

    if (pages) {

        ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));

    }

    if (page->next) {

        prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);

        prev->next = page->next;

        page->next->prev = page->prev;

    }

    join = page + page->slab;

    if (join < pool->last) {

        type = join->prev & NGX_SLAB_PAGE_MASK;

        if (type == NGX_SLAB_PAGE) {

            if (join->next != NULL) {

                pages += join->slab;

                page->slab += join->slab;

                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);

                prev->next = join->next;

                join->next->prev = join->prev;

                join->slab = NGX_SLAB_PAGE_FREE;

                join->next = NULL;

                join->prev = NGX_SLAB_PAGE;

            }

        }

    }

    if (page > pool->pages) {

        join = page - 1;

        type = join->prev & NGX_SLAB_PAGE_MASK;

        if (type == NGX_SLAB_PAGE) {

            if (join->slab == NGX_SLAB_PAGE_FREE) {

                join = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);

            }

            if (join->next != NULL) {

                pages += join->slab;

                join->slab += page->slab;

                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);

                prev->next = join->next;

                join->next->prev = join->prev;

                page->slab = NGX_SLAB_PAGE_FREE;

                page->next = NULL;

                page->prev = NGX_SLAB_PAGE;

                page = join;

            }

        }

    }

    if (pages) {

        page[pages].prev = (uintptr_t) page;

    }

    page->prev = (uintptr_t) &pool->free;

    page->next = pool->free.next;

    page->next->prev = (uintptr_t) page;

    pool->free.next = page;

}

static void

‌ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)

{

    ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx);

}

src/os/unix/ngx_darwin_config.h - nginx-1.7.9

Macros defined

IOV_MAX

NGX_HAVE_CASELESS_FILESYSTEM

NGX_HAVE_DEBUG_MALLOC

NGX_HAVE_INHERITED_NONBLOCK

NGX_HAVE_OS_SPECIFIC_INIT

NGX_LISTEN_BACKLOG

_NGX_DARWIN_CONFIG_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_

‌#define _NGX_DARWIN_CONFIG_H_INCLUDED_

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>

#include <inttypes.h>

#include <stdarg.h>

#include <stddef.h>             /* offsetof() */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <pwd.h>

#include <grp.h>

#include <dirent.h>

#include <glob.h>

#include <sys/mount.h>          /* statfs() */

#include <sys/filio.h>          /* FIONBIO */

#include <sys/ioctl.h>

#include <sys/uio.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/mman.h>

#include <sys/resource.h>

#include <sched.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>        /* TCP_NODELAY */

#include <arpa/inet.h>

#include <netdb.h>

#include <sys/un.h>

#include <sys/sysctl.h>

#include <xlocale.h>

#ifndef IOV_MAX

‌#define IOV_MAX   64

#endif

#include <ngx_auto_config.h>

#if (NGX_HAVE_POSIX_SEM)

#include <semaphore.h>

#endif

#if (NGX_HAVE_POLL)

#include <poll.h>

#endif

#if (NGX_HAVE_KQUEUE)

#include <sys/event.h>

#endif

‌#define NGX_LISTEN_BACKLOG  -1

#ifndef NGX_HAVE_INHERITED_NONBLOCK

‌#define NGX_HAVE_INHERITED_NONBLOCK  1

#endif

#ifndef NGX_HAVE_CASELESS_FILESYSTEM

‌#define NGX_HAVE_CASELESS_FILESYSTEM  1

#endif

‌#define NGX_HAVE_OS_SPECIFIC_INIT    1

‌#define NGX_HAVE_DEBUG_MALLOC        1

extern char **environ;

#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */

src/core/ngx_array.c - nginx-1.7.9

Functions defined

ngx_array_create

ngx_array_destroy

ngx_array_push

ngx_array_push_n

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

ngx_array_t *

‌ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)

{

    ngx_array_t *a;

    a = ngx_palloc(p, sizeof(ngx_array_t));

    if (a == NULL) {

        return NULL;

    }

    if (ngx_array_init(a, p, n, size) != NGX_OK) {

        return NULL;

    }

    return a;

}

void

‌ngx_array_destroy(ngx_array_t *a)

{

    ngx_pool_t  *p;

    p = a->pool;

    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {

        p->d.last -= a->size * a->nalloc;

    }

    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {

        p->d.last = (u_char *) a;

    }

}

void *

‌ngx_array_push(ngx_array_t *a)

{

    void        *elt, *new;

    size_t       size;

    ngx_pool_t  *p;

    if (a->nelts == a->nalloc) {

        /* the array is full */

        size = a->size * a->nalloc;

        p = a->pool;

        if ((u_char *) a->elts + size == p->d.last

            && p->d.last + a->size <= p->d.end)

        {

            /*

             * the array allocation is the last in the pool

             * and there is space for new allocation

             */

            p->d.last += a->size;

            a->nalloc++;

        } else {

            /* allocate a new array */

            new = ngx_palloc(p, 2 * size);

            if (new == NULL) {

                return NULL;

            }

            ngx_memcpy(new, a->elts, size);

            a->elts = new;

            a->nalloc *= 2;

        }

    }

    elt = (u_char *) a->elts + a->size * a->nelts;

    a->nelts++;

    return elt;

}

void *

‌ngx_array_push_n(ngx_array_t *a, ngx_uint_t n)

{

    void        *elt, *new;

    size_t       size;

    ngx_uint_t   nalloc;

    ngx_pool_t  *p;

    size = n * a->size;

    if (a->nelts + n > a->nalloc) {

        /* the array is full */

        p = a->pool;

        if ((u_char *) a->elts + a->size * a->nalloc == p->d.last

            && p->d.last + size <= p->d.end)

        {

            /*

             * the array allocation is the last in the pool

             * and there is space for new allocation

             */

            p->d.last += size;

            a->nalloc += n;

        } else {

            /* allocate a new array */

            nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);

            new = ngx_palloc(p, nalloc * a->size);

            if (new == NULL) {

                return NULL;

            }

            ngx_memcpy(new, a->elts, a->nelts * a->size);

            a->elts = new;

            a->nalloc = nalloc;

        }

    }

    elt = (u_char *) a->elts + a->size * a->nelts;

    a->nelts += n;

    return elt;

}

src/event/ngx_event_posted.h - nginx-1.7.9

Macros defined

_NGX_EVENT_POSTED_H_INCLUDED_

ngx_delete_posted_event

ngx_post_event

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_POSTED_H_INCLUDED_

‌#define _NGX_EVENT_POSTED_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define ngx_post_event(ev, q)                                                 \

                                                                              \

    if (!ev->posted) {                                                        \

        ev->posted = 1;                                                       \

        ngx_queue_insert_tail(q, &ev->queue);                                 \

                                                                              \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "post event %p", ev);  \

                                                                              \

    } else  {                                                                 \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,                        \

                       "update posted event %p", ev);                         \

    }

‌#define ngx_delete_posted_event(ev)                                           \

                                                                              \

    ev->posted = 0;                                                           \

    ngx_queue_remove(&ev->queue);                                             \

                                                                              \

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,                            \

                   "delete posted event %p", ev);

void ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted);

extern ngx_queue_t  ngx_posted_accept_events;

extern ngx_queue_t  ngx_posted_events;

#endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */

src/event/ngx_event_posted.c - nginx-1.7.9

Global variables defined

ngx_posted_accept_events

ngx_posted_events

Functions defined

ngx_event_process_posted

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌ngx_queue_t  ngx_posted_accept_events;

‌ngx_queue_t  ngx_posted_events;

void

‌ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)

{

    ngx_queue_t  *q;

    ngx_event_t  *ev;

    while (!ngx_queue_empty(posted)) {

        q = ngx_queue_head(posted);

        ev = ngx_queue_data(q, ngx_event_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                      "posted event %p", ev);

        ngx_delete_posted_event(ev);

        ev->handler(ev);

    }

}

src/core/ngx_crc32.h - nginx-1.7.9

Functions defined

ngx_crc32_long

ngx_crc32_short

ngx_crc32_update

Macros defined

_NGX_CRC32_H_INCLUDED_

ngx_crc32_final

ngx_crc32_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CRC32_H_INCLUDED_

‌#define _NGX_CRC32_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

extern uint32_t  *ngx_crc32_table_short;

extern uint32_t   ngx_crc32_table256[];

static ngx_inline uint32_t

‌ngx_crc32_short(u_char *p, size_t len)

{

    u_char    c;

    uint32_t  crc;

    crc = 0xffffffff;

    while (len--) {

        c = *p++;

        crc = ngx_crc32_table_short[(crc ^ (c & 0xf)) & 0xf] ^ (crc >> 4);

        crc = ngx_crc32_table_short[(crc ^ (c >> 4)) & 0xf] ^ (crc >> 4);

    }

    return crc ^ 0xffffffff;

}

static ngx_inline uint32_t

‌ngx_crc32_long(u_char *p, size_t len)

{

    uint32_t  crc;

    crc = 0xffffffff;

    while (len--) {

        crc = ngx_crc32_table256[(crc ^ *p++) & 0xff] ^ (crc >> 8);

    }

    return crc ^ 0xffffffff;

}

‌#define ngx_crc32_init(crc)                                                   \

    crc = 0xffffffff

static ngx_inline void

‌ngx_crc32_update(uint32_t *crc, u_char *p, size_t len)

{

    uint32_t  c;

    c = *crc;

    while (len--) {

        c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8);

    }

    *crc = c;

}

‌#define ngx_crc32_final(crc)                                                  \

    crc ^= 0xffffffff

ngx_int_t ngx_crc32_table_init(void);

#endif /* _NGX_CRC32_H_INCLUDED_ */

src/core/ngx_crc32.c - nginx-1.7.9

Global variables defined

ngx_crc32_table16

ngx_crc32_table256

ngx_crc32_table_short

Functions defined

ngx_crc32_table_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* The code and lookup tables are based on the algorithm

* described at http://www.w3.org/TR/PNG/

*

* The 256 element lookup table takes 1024 bytes, and it may be completely

* cached after processing about 30-60 bytes of data.  So for short data

* we use the 16 element lookup table that takes only 64 bytes and align it

* to CPU cache line size.  Of course, the small table adds code inside

* CRC32 loop, but the cache misses overhead is bigger than overhead of

* the additional code.  For example, ngx_crc32_short() of 16 bytes of data

* takes half as much CPU clocks than ngx_crc32_long().

*/

‌static uint32_t  ngx_crc32_table16[] = {

    0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,

    0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,

    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,

    0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c

};

‌uint32_t  ngx_crc32_table256[] = {

    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,

    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,

    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,

    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,

    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,

    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,

    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,

    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,

    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,

    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,

    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,

    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,

    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,

    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,

    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,

    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,

    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,

    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,

    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,

    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,

    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,

    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,

    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,

    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,

    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,

    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,

    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,

    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,

    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,

    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,

    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,

    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,

    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,

    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,

    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,

    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,

    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,

    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,

    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,

    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,

    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,

    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,

    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,

    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,

    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,

    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,

    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,

    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,

    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,

    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,

    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,

    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,

    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,

    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,

    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,

    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,

    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,

    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,

    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,

    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,

    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,

    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,

    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,

    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d

};

‌uint32_t *ngx_crc32_table_short = ngx_crc32_table16;

ngx_int_t

‌ngx_crc32_table_init(void)

{

    void  *p;

    if (((uintptr_t) ngx_crc32_table_short

          & ~((uintptr_t) ngx_cacheline_size - 1))

        == (uintptr_t) ngx_crc32_table_short)

    {

        return NGX_OK;

    }

    p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);

    if (p == NULL) {

        return NGX_ERROR;

    }

    p = ngx_align_ptr(p, ngx_cacheline_size);

    ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));

    ngx_crc32_table_short = p;

    return NGX_OK;

}

src/event/ngx_event_openssl_stapling.c - nginx-1.7.9

Data types defined

ngx_ssl_ocsp_ctx_s

ngx_ssl_ocsp_ctx_t

ngx_ssl_stapling_t

Functions defined

ngx_ssl_certificate_status_callback

ngx_ssl_ocsp_connect

ngx_ssl_ocsp_create_request

ngx_ssl_ocsp_done

ngx_ssl_ocsp_dummy_handler

ngx_ssl_ocsp_error

ngx_ssl_ocsp_log_error

ngx_ssl_ocsp_parse_header_line

ngx_ssl_ocsp_parse_status_line

ngx_ssl_ocsp_process_body

ngx_ssl_ocsp_process_headers

ngx_ssl_ocsp_process_status_line

ngx_ssl_ocsp_read_handler

ngx_ssl_ocsp_request

ngx_ssl_ocsp_resolve_handler

ngx_ssl_ocsp_start

ngx_ssl_ocsp_write_handler

ngx_ssl_stapling

ngx_ssl_stapling

ngx_ssl_stapling_cleanup

ngx_ssl_stapling_file

ngx_ssl_stapling_issuer

ngx_ssl_stapling_ocsp_handler

ngx_ssl_stapling_resolver

ngx_ssl_stapling_resolver

ngx_ssl_stapling_responder

ngx_ssl_stapling_update

Source code

/*

* Copyright (C) Maxim Dounin

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)

typedef struct {

    ngx_str_t                    staple;

    ngx_msec_t                   timeout;

    ngx_resolver_t              *resolver;

    ngx_msec_t                   resolver_timeout;

    ngx_addr_t                  *addrs;

    ngx_str_t                    host;

    ngx_str_t                    uri;

    in_port_t                    port;

    SSL_CTX                     *ssl_ctx;

    X509                        *cert;

    X509                        *issuer;

    time_t                       valid;

    unsigned                     verify:1;

    unsigned                     loading:1;

‌} ngx_ssl_stapling_t;

‌typedef struct ngx_ssl_ocsp_ctx_s  ngx_ssl_ocsp_ctx_t;

‌struct ngx_ssl_ocsp_ctx_s {

    X509                        *cert;

    X509                        *issuer;

    ngx_uint_t                   naddrs;

    ngx_addr_t                  *addrs;

    ngx_str_t                    host;

    ngx_str_t                    uri;

    in_port_t                    port;

    ngx_resolver_t              *resolver;

    ngx_msec_t                   resolver_timeout;

    ngx_msec_t                   timeout;

    void                       (*handler)(ngx_ssl_ocsp_ctx_t *r);

    void                        *data;

    ngx_buf_t                   *request;

    ngx_buf_t                   *response;

    ngx_peer_connection_t        peer;

    ngx_int_t                  (*process)(ngx_ssl_ocsp_ctx_t *r);

    ngx_uint_t                   state;

    ngx_uint_t                   code;

    ngx_uint_t                   count;

    ngx_uint_t                   done;

    u_char                      *header_name_start;

    u_char                      *header_name_end;

    u_char                      *header_start;

    u_char                      *header_end;

    ngx_pool_t                  *pool;

    ngx_log_t                   *log;

};

static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *file);

static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);

static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_str_t *responder);

static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,

    void *data);

static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);

static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);

static void ngx_ssl_stapling_cleanup(void *data);

static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);

static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);

static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);

static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);

static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);

static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);

static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);

static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);

static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);

static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);

static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);

static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);

static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);

static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);

static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);

ngx_int_t

‌ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,

    ngx_str_t *responder, ngx_uint_t verify)

{

    ngx_int_t                  rc;

    ngx_pool_cleanup_t        *cln;

    ngx_ssl_stapling_t        *staple;

    staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));

    if (staple == NULL) {

        return NGX_ERROR;

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_ERROR;

    }

    cln->handler = ngx_ssl_stapling_cleanup;

    cln->data = staple;

    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_set_ex_data() failed");

        return NGX_ERROR;

    }

    staple->ssl_ctx = ssl->ctx;

    staple->timeout = 60000;

    staple->verify = verify;

    if (file->len) {

        /* use OCSP response from the file */

        if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {

            return NGX_ERROR;

        }

        goto done;

    }

    rc = ngx_ssl_stapling_issuer(cf, ssl);

    if (rc == NGX_DECLINED) {

        return NGX_OK;

    }

    if (rc != NGX_OK) {

        return NGX_ERROR;

    }

    rc = ngx_ssl_stapling_responder(cf, ssl, responder);

    if (rc == NGX_DECLINED) {

        return NGX_OK;

    }

    if (rc != NGX_OK) {

        return NGX_ERROR;

    }

done:

    SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);

    SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);

    return NGX_OK;

}

static ngx_int_t

‌ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)

{

    BIO                 *bio;

    int                  len;

    u_char              *p, *buf;

    OCSP_RESPONSE       *response;

    ngx_ssl_stapling_t  *staple;

    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    bio = BIO_new_file((char *) file->data, "r");

    if (bio == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "BIO_new_file(\"%s\") failed", file->data);

        return NGX_ERROR;

    }

    response = d2i_OCSP_RESPONSE_bio(bio, NULL);

    if (response == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);

        BIO_free(bio);

        return NGX_ERROR;

    }

    len = i2d_OCSP_RESPONSE(response, NULL);

    if (len <= 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);

        goto failed;

    }

    buf = ngx_alloc(len, ssl->log);

    if (buf == NULL) {

        goto failed;

    }

    p = buf;

    len = i2d_OCSP_RESPONSE(response, &p);

    if (len <= 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);

        ngx_free(buf);

        goto failed;

    }

    OCSP_RESPONSE_free(response);

    BIO_free(bio);

    staple->staple.data = buf;

    staple->staple.len = len;

    return NGX_OK;

failed:

    OCSP_RESPONSE_free(response);

    BIO_free(bio);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)

{

    int                  i, n, rc;

    X509                *cert, *issuer;

    X509_STORE          *store;

    X509_STORE_CTX      *store_ctx;

    STACK_OF(X509)      *chain;

    ngx_ssl_stapling_t  *staple;

    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);

    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);

#if OPENSSL_VERSION_NUMBER >= 0x10001000L

    SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);

#else

    chain = ssl->ctx->extra_certs;

#endif

    n = sk_X509_num(chain);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,

                   "SSL get issuer: %d extra certs", n);

    for (i = 0; i < n; i++) {

        issuer = sk_X509_value(chain, i);

        if (X509_check_issued(issuer, cert) == X509_V_OK) {

            CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,

                           "SSL get issuer: found %p in extra certs", issuer);

            staple->cert = cert;

            staple->issuer = issuer;

            return NGX_OK;

        }

    }

    store = SSL_CTX_get_cert_store(ssl->ctx);

    if (store == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "SSL_CTX_get_cert_store() failed");

        return NGX_ERROR;

    }

    store_ctx = X509_STORE_CTX_new();

    if (store_ctx == NULL) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_STORE_CTX_new() failed");

        return NGX_ERROR;

    }

    if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_STORE_CTX_init() failed");

        return NGX_ERROR;

    }

    rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);

    if (rc == -1) {

        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,

                      "X509_STORE_CTX_get1_issuer() failed");

        X509_STORE_CTX_free(store_ctx);

        return NGX_ERROR;

    }

    if (rc == 0) {

        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                      "\"ssl_stapling\" ignored, issuer certificate not found");

        X509_STORE_CTX_free(store_ctx);

        return NGX_DECLINED;

    }

    X509_STORE_CTX_free(store_ctx);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,

                   "SSL get issuer: found %p in cert store", issuer);

    staple->cert = cert;

    staple->issuer = issuer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)

{

    ngx_url_t                  u;

    char                      *s;

    ngx_ssl_stapling_t        *staple;

    STACK_OF(OPENSSL_STRING)  *aia;

    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);

    if (responder->len == 0) {

        /* extract OCSP responder URL from certificate */

        aia = X509_get1_ocsp(staple->cert);

        if (aia == NULL) {

            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                          "\"ssl_stapling\" ignored, "

                          "no OCSP responder URL in the certificate");

            return NGX_DECLINED;

        }

#if OPENSSL_VERSION_NUMBER >= 0x10000000L

        s = sk_OPENSSL_STRING_value(aia, 0);

#else

        s = sk_value(aia, 0);

#endif

        if (s == NULL) {

            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                          "\"ssl_stapling\" ignored, "

                          "no OCSP responder URL in the certificate");

            X509_email_free(aia);

            return NGX_DECLINED;

        }

        responder->len = ngx_strlen(s);

        responder->data = ngx_palloc(cf->pool, responder->len);

        if (responder->data == NULL) {

            X509_email_free(aia);

            return NGX_ERROR;

        }

        ngx_memcpy(responder->data, s, responder->len);

        X509_email_free(aia);

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = *responder;

    u.default_port = 80;

    u.uri_part = 1;

    if (u.url.len > 7

        && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)

    {

        u.url.len -= 7;

        u.url.data += 7;

    } else {

        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                      "\"ssl_stapling\" ignored, "

                      "invalid URL prefix in OCSP responder \"%V\"", &u.url);

        return NGX_DECLINED;

    }

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                          "\"ssl_stapling\" ignored, "

                          "%s in OCSP responder \"%V\"", u.err, &u.url);

            return NGX_DECLINED;

        }

        return NGX_ERROR;

    }

    staple->addrs = u.addrs;

    staple->host = u.host;

    staple->uri = u.uri;

    staple->port = u.port;

    if (staple->uri.len == 0) {

        ngx_str_set(&staple->uri, "/");

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)

{

    ngx_ssl_stapling_t  *staple;

    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);

    staple->resolver = resolver;

    staple->resolver_timeout = resolver_timeout;

    return NGX_OK;

}

static int

‌ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)

{

    int                  rc;

    u_char              *p;

    ngx_connection_t    *c;

    ngx_ssl_stapling_t  *staple;

    c = ngx_ssl_get_connection(ssl_conn);

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "SSL certificate status callback");

    staple = data;

    rc = SSL_TLSEXT_ERR_NOACK;

    if (staple->staple.len) {

        /* we have to copy ocsp response as OpenSSL will free it by itself */

        p = OPENSSL_malloc(staple->staple.len);

        if (p == NULL) {

            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");

            return SSL_TLSEXT_ERR_NOACK;

        }

        ngx_memcpy(p, staple->staple.data, staple->staple.len);

        SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);

        rc = SSL_TLSEXT_ERR_OK;

    }

    ngx_ssl_stapling_update(staple);

    return rc;

}

static void

‌ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)

{

    ngx_ssl_ocsp_ctx_t  *ctx;

    if (staple->host.len == 0

        || staple->loading || staple->valid >= ngx_time())

    {

        return;

    }

    staple->loading = 1;

    ctx = ngx_ssl_ocsp_start();

    if (ctx == NULL) {

        return;

    }

    ctx->cert = staple->cert;

    ctx->issuer = staple->issuer;

    ctx->addrs = staple->addrs;

    ctx->host = staple->host;

    ctx->uri = staple->uri;

    ctx->port = staple->port;

    ctx->timeout = staple->timeout;

    ctx->resolver = staple->resolver;

    ctx->resolver_timeout = staple->resolver_timeout;

    ctx->handler = ngx_ssl_stapling_ocsp_handler;

    ctx->data = staple;

    ngx_ssl_ocsp_request(ctx);

    return;

}

static void

‌ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)

{

#if OPENSSL_VERSION_NUMBER >= 0x0090707fL

    const

#endif

    u_char                *p;

    int                    n;

    size_t                 len;

    ngx_str_t              response;

    X509_STORE            *store;

    STACK_OF(X509)        *chain;

    OCSP_CERTID           *id;

    OCSP_RESPONSE         *ocsp;

    OCSP_BASICRESP        *basic;

    ngx_ssl_stapling_t    *staple;

    ASN1_GENERALIZEDTIME  *thisupdate, *nextupdate;

    staple = ctx->data;

    ocsp = NULL;

    basic = NULL;

    id = NULL;

    if (ctx->code != 200) {

        goto error;

    }

    /* check the response */

    len = ctx->response->last - ctx->response->pos;

    p = ctx->response->pos;

    ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);

    if (ocsp == NULL) {

        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,

                      "d2i_OCSP_RESPONSE() failed");

        goto error;

    }

    n = OCSP_response_status(ocsp);

    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {

        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                      "OCSP response not successful (%d: %s)",

                      n, OCSP_response_status_str(n));

        goto error;

    }

    basic = OCSP_response_get1_basic(ocsp);

    if (basic == NULL) {

        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,

                      "OCSP_response_get1_basic() failed");

        goto error;

    }

    store = SSL_CTX_get_cert_store(staple->ssl_ctx);

    if (store == NULL) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "SSL_CTX_get_cert_store() failed");

        goto error;

    }

#if OPENSSL_VERSION_NUMBER >= 0x10001000L

    SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);

#else

    chain = staple->ssl_ctx->extra_certs;

#endif

    if (OCSP_basic_verify(basic, chain, store,

                          staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)

        != 1)

    {

        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,

                      "OCSP_basic_verify() failed");

        goto error;

    }

    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);

    if (id == NULL) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "OCSP_cert_to_id() failed");

        goto error;

    }

    if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,

                              &thisupdate, &nextupdate)

        != 1)

    {

        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                      "certificate status not found in the OCSP response");

        goto error;

    }

    if (n != V_OCSP_CERTSTATUS_GOOD) {

        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                      "certificate status \"%s\" in the OCSP response",

                      OCSP_cert_status_str(n));

        goto error;

    }

    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {

        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,

                      "OCSP_check_validity() failed");

        goto error;

    }

    OCSP_CERTID_free(id);

    OCSP_BASICRESP_free(basic);

    OCSP_RESPONSE_free(ocsp);

    /* copy the response to memory not in ctx->pool */

    response.len = len;

    response.data = ngx_alloc(response.len, ctx->log);

    if (response.data == NULL) {

        goto done;

    }

    ngx_memcpy(response.data, ctx->response->pos, response.len);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp response, %s, %uz",

                   OCSP_cert_status_str(n), response.len);

    if (staple->staple.data) {

        ngx_free(staple->staple.data);

    }

    staple->staple = response;

done:

    staple->loading = 0;

    staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */

    ngx_ssl_ocsp_done(ctx);

    return;

error:

    staple->loading = 0;

    staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */

    if (id) {

        OCSP_CERTID_free(id);

    }

    if (basic) {

        OCSP_BASICRESP_free(basic);

    }

    if (ocsp) {

        OCSP_RESPONSE_free(ocsp);

    }

    ngx_ssl_ocsp_done(ctx);

}

static void

‌ngx_ssl_stapling_cleanup(void *data)

{

    ngx_ssl_stapling_t  *staple = data;

    if (staple->issuer) {

        X509_free(staple->issuer);

    }

    if (staple->staple.data) {

        ngx_free(staple->staple.data);

    }

}

static ngx_ssl_ocsp_ctx_t *

‌ngx_ssl_ocsp_start(void)

{

    ngx_log_t           *log;

    ngx_pool_t          *pool;

    ngx_ssl_ocsp_ctx_t  *ctx;

    pool = ngx_create_pool(2048, ngx_cycle->log);

    if (pool == NULL) {

        return NULL;

    }

    ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));

    if (ctx == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    log = ngx_palloc(pool, sizeof(ngx_log_t));

    if (log == NULL) {

        ngx_destroy_pool(pool);

        return NULL;

    }

    ctx->pool = pool;

    *log = *ctx->pool->log;

    ctx->pool->log = log;

    ctx->log = log;

    log->handler = ngx_ssl_ocsp_log_error;

    log->data = ctx;

    log->action = "requesting certificate status";

    return ctx;

}

static void

‌ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp done");

    if (ctx->peer.connection) {

        ngx_close_connection(ctx->peer.connection);

    }

    ngx_destroy_pool(ctx->pool);

}

static void

‌ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp error");

    ctx->code = 0;

    ctx->handler(ctx);

}

static void

‌ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_resolver_ctx_t  *resolve, temp;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp request");

    if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {

        ngx_ssl_ocsp_error(ctx);

        return;

    }

    if (ctx->resolver) {

        /* resolve OCSP responder hostname */

        temp.name = ctx->host;

        resolve = ngx_resolve_start(ctx->resolver, &temp);

        if (resolve == NULL) {

            ngx_ssl_ocsp_error(ctx);

            return;

        }

        if (resolve == NGX_NO_RESOLVER) {

            ngx_log_error(NGX_LOG_WARN, ctx->log, 0,

                          "no resolver defined to resolve %V", &ctx->host);

            goto connect;

        }

        resolve->name = ctx->host;

        resolve->handler = ngx_ssl_ocsp_resolve_handler;

        resolve->data = ctx;

        resolve->timeout = ctx->resolver_timeout;

        if (ngx_resolve_name(resolve) != NGX_OK) {

            ngx_ssl_ocsp_error(ctx);

            return;

        }

        return;

    }

connect:

    ngx_ssl_ocsp_connect(ctx);

}

static void

‌ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)

{

    ngx_ssl_ocsp_ctx_t *ctx = resolve->data;

    u_char           *p;

    size_t            len;

    in_port_t         port;

    socklen_t         socklen;

    ngx_uint_t        i;

    struct sockaddr  *sockaddr;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp resolve handler");

    if (resolve->state) {

        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                      "%V could not be resolved (%i: %s)",

                      &resolve->name, resolve->state,

                      ngx_resolver_strerror(resolve->state));

        goto failed;

    }

#if (NGX_DEBUG)

    {

    u_char     text[NGX_SOCKADDR_STRLEN];

    ngx_str_t  addr;

    addr.data = text;

    for (i = 0; i < resolve->naddrs; i++) {

        addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr,

                                 resolve->addrs[i].socklen,

                                 text, NGX_SOCKADDR_STRLEN, 0);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                       "name was resolved to %V", &addr);

    }

    }

#endif

    ctx->naddrs = resolve->naddrs;

    ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));

    if (ctx->addrs == NULL) {

        goto failed;

    }

    port = htons(ctx->port);

    for (i = 0; i < resolve->naddrs; i++) {

        socklen = resolve->addrs[i].socklen;

        sockaddr = ngx_palloc(ctx->pool, socklen);

        if (sockaddr == NULL) {

            goto failed;

        }

        ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen);

        switch (sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            ((struct sockaddr_in6 *) sockaddr)->sin6_port = port;

            break;

#endif

        default: /* AF_INET */

            ((struct sockaddr_in *) sockaddr)->sin_port = port;

        }

        ctx->addrs[i].sockaddr = sockaddr;

        ctx->addrs[i].socklen = socklen;

        p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN);

        if (p == NULL) {

            goto failed;

        }

        len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);

        ctx->addrs[i].name.len = len;

        ctx->addrs[i].name.data = p;

    }

    ngx_resolve_name_done(resolve);

    ngx_ssl_ocsp_connect(ctx);

    return;

failed:

    ngx_resolve_name_done(resolve);

    ngx_ssl_ocsp_error(ctx);

}

static void

‌ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_int_t    rc;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp connect");

    /* TODO: use all ip addresses */

    ctx->peer.sockaddr = ctx->addrs[0].sockaddr;

    ctx->peer.socklen = ctx->addrs[0].socklen;

    ctx->peer.name = &ctx->addrs[0].name;

    ctx->peer.get = ngx_event_get_peer;

    ctx->peer.log = ctx->log;

    ctx->peer.log_error = NGX_ERROR_ERR;

    rc = ngx_event_connect_peer(&ctx->peer);

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp connect peer done");

    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {

        ngx_ssl_ocsp_error(ctx);

        return;

    }

    ctx->peer.connection->data = ctx;

    ctx->peer.connection->pool = ctx->pool;

    ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;

    ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;

    ctx->process = ngx_ssl_ocsp_process_status_line;

    ngx_add_timer(ctx->peer.connection->read, ctx->timeout);

    ngx_add_timer(ctx->peer.connection->write, ctx->timeout);

    if (rc == NGX_OK) {

        ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);

        return;

    }

}

static void

‌ngx_ssl_ocsp_write_handler(ngx_event_t *wev)

{

    ssize_t              n, size;

    ngx_connection_t    *c;

    ngx_ssl_ocsp_ctx_t  *ctx;

    c = wev->data;

    ctx = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,

                   "ssl ocsp write handler");

    if (wev->timedout) {

        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,

                      "OCSP responder timed out");

        ngx_ssl_ocsp_error(ctx);

        return;

    }

    size = ctx->request->last - ctx->request->pos;

    n = ngx_send(c, ctx->request->pos, size);

    if (n == NGX_ERROR) {

        ngx_ssl_ocsp_error(ctx);

        return;

    }

    if (n > 0) {

        ctx->request->pos += n;

        if (n == size) {

            wev->handler = ngx_ssl_ocsp_dummy_handler;

            if (wev->timer_set) {

                ngx_del_timer(wev);

            }

            if (ngx_handle_write_event(wev, 0) != NGX_OK) {

                ngx_ssl_ocsp_error(ctx);

            }

            return;

        }

    }

    if (!wev->timer_set) {

        ngx_add_timer(wev, ctx->timeout);

    }

}

static void

‌ngx_ssl_ocsp_read_handler(ngx_event_t *rev)

{

    ssize_t            n, size;

    ngx_int_t          rc;

    ngx_ssl_ocsp_ctx_t    *ctx;

    ngx_connection_t  *c;

    c = rev->data;

    ctx = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,

                   "ssl ocsp read handler");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,

                      "OCSP responder timed out");

        ngx_ssl_ocsp_error(ctx);

        return;

    }

    if (ctx->response == NULL) {

        ctx->response = ngx_create_temp_buf(ctx->pool, 16384);

        if (ctx->response == NULL) {

            ngx_ssl_ocsp_error(ctx);

            return;

        }

    }

    for ( ;; ) {

        size = ctx->response->end - ctx->response->last;

        n = ngx_recv(c, ctx->response->last, size);

        if (n > 0) {

            ctx->response->last += n;

            rc = ctx->process(ctx);

            if (rc == NGX_ERROR) {

                ngx_ssl_ocsp_error(ctx);

                return;

            }

            continue;

        }

        if (n == NGX_AGAIN) {

            if (ngx_handle_read_event(rev, 0) != NGX_OK) {

                ngx_ssl_ocsp_error(ctx);

            }

            return;

        }

        break;

    }

    ctx->done = 1;

    rc = ctx->process(ctx);

    if (rc == NGX_DONE) {

        /* ctx->handler() was called */

        return;

    }

    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                  "OCSP responder prematurely closed connection");

    ngx_ssl_ocsp_error(ctx);

}

static void

‌ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)

{

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "ssl ocsp dummy handler");

}

static ngx_int_t

‌ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)

{

    int            len;

    u_char        *p;

    uintptr_t      escape;

    ngx_str_t      binary, base64;

    ngx_buf_t     *b;

    OCSP_CERTID   *id;

    OCSP_REQUEST  *ocsp;

    ocsp = OCSP_REQUEST_new();

    if (ocsp == NULL) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "OCSP_REQUEST_new() failed");

        return NGX_ERROR;

    }

    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);

    if (id == NULL) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "OCSP_cert_to_id() failed");

        goto failed;

    }

    if (OCSP_request_add0_id(ocsp, id) == NULL) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "OCSP_request_add0_id() failed");

        goto failed;

    }

    len = i2d_OCSP_REQUEST(ocsp, NULL);

    if (len <= 0) {

        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,

                      "i2d_OCSP_REQUEST() failed");

        goto failed;

    }

    binary.len = len;

    binary.data = ngx_palloc(ctx->pool, len);

    if (binary.data == NULL) {

        goto failed;

    }

    p = binary.data;

    len = i2d_OCSP_REQUEST(ocsp, &p);

    if (len <= 0) {

        ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,

                      "i2d_OCSP_REQUEST() failed");

        goto failed;

    }

    base64.len = ngx_base64_encoded_length(binary.len);

    base64.data = ngx_palloc(ctx->pool, base64.len);

    if (base64.data == NULL) {

        goto failed;

    }

    ngx_encode_base64(&base64, &binary);

    escape = ngx_escape_uri(NULL, base64.data, base64.len,

                            NGX_ESCAPE_URI_COMPONENT);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp request length %z, escape %d",

                   base64.len, escape);

    len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1

          + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1

          + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1

          + sizeof(CRLF) - 1;

    b = ngx_create_temp_buf(ctx->pool, len);

    if (b == NULL) {

        goto failed;

    }

    p = b->last;

    p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);

    p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);

    if (ctx->uri.data[ctx->uri.len - 1] != '/') {

        *p++ = '/';

    }

    if (escape == 0) {

        p = ngx_cpymem(p, base64.data, base64.len);

    } else {

        p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,

                                      NGX_ESCAPE_URI_COMPONENT);

    }

    p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);

    p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);

    p = ngx_cpymem(p, ctx->host.data, ctx->host.len);

    *p++ = CR; *p++ = LF;

    /* add "\r\n" at the header end */

    *p++ = CR; *p++ = LF;

    b->last = p;

    ctx->request = b;

    OCSP_REQUEST_free(ocsp);

    return NGX_OK;

failed:

    OCSP_REQUEST_free(ocsp);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_int_t  rc;

    rc = ngx_ssl_ocsp_parse_status_line(ctx);

    if (rc == NGX_OK) {

#if 0

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                       "ssl ocsp status line \"%*s\"",

                       ctx->response->pos - ctx->response->start,

                       ctx->response->start);

#endif

        ctx->process = ngx_ssl_ocsp_process_headers;

        return ctx->process(ctx);

    }

    if (rc == NGX_AGAIN) {

        return NGX_AGAIN;

    }

    /* rc == NGX_ERROR */

    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                  "OCSP responder sent invalid response");

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)

{

    u_char      ch;

    u_char     *p;

    ngx_buf_t  *b;

    enum {

        sw_start = 0,

        sw_H,

        sw_HT,

        sw_HTT,

        sw_HTTP,

        sw_first_major_digit,

        sw_major_digit,

        sw_first_minor_digit,

        sw_minor_digit,

        sw_status,

        sw_space_after_status,

        sw_status_text,

        sw_almost_done

    } state;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp process status line");

    state = ctx->state;

    b = ctx->response;

    for (p = b->pos; p < b->last; p++) {

        ch = *p;

        switch (state) {

        /* "HTTP/" */

        case sw_start:

            switch (ch) {

            case 'H':

                state = sw_H;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_H:

            switch (ch) {

            case 'T':

                state = sw_HT;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HT:

            switch (ch) {

            case 'T':

                state = sw_HTT;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HTT:

            switch (ch) {

            case 'P':

                state = sw_HTTP;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        case sw_HTTP:

            switch (ch) {

            case '/':

                state = sw_first_major_digit;

                break;

            default:

                return NGX_ERROR;

            }

            break;

        /* the first digit of major HTTP version */

        case sw_first_major_digit:

            if (ch < '1' || ch > '9') {

                return NGX_ERROR;

            }

            state = sw_major_digit;

            break;

        /* the major HTTP version or dot */

        case sw_major_digit:

            if (ch == '.') {

                state = sw_first_minor_digit;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            break;

        /* the first digit of minor HTTP version */

        case sw_first_minor_digit:

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            state = sw_minor_digit;

            break;

        /* the minor HTTP version or the end of the request line */

        case sw_minor_digit:

            if (ch == ' ') {

                state = sw_status;

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            break;

        /* HTTP status code */

        case sw_status:

            if (ch == ' ') {

                break;

            }

            if (ch < '0' || ch > '9') {

                return NGX_ERROR;

            }

            ctx->code = ctx->code * 10 + ch - '0';

            if (++ctx->count == 3) {

                state = sw_space_after_status;

            }

            break;

        /* space or end of line */

        case sw_space_after_status:

            switch (ch) {

            case ' ':

                state = sw_status_text;

                break;

            case '.':                    /* IIS may send 403.1, 403.2, etc */

                state = sw_status_text;

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

            break;

        /* any text until end of line */

        case sw_status_text:

            switch (ch) {

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            }

            break;

        /* end of status line */

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

        }

    }

    b->pos = p;

    ctx->state = state;

    return NGX_AGAIN;

done:

    b->pos = p + 1;

    ctx->state = sw_start;

    return NGX_OK;

}

static ngx_int_t

‌ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)

{

    size_t     len;

    ngx_int_t  rc;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp process headers");

    for ( ;; ) {

        rc = ngx_ssl_ocsp_parse_header_line(ctx);

        if (rc == NGX_OK) {

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                           "ssl ocsp header \"%*s: %*s\"",

                           ctx->header_name_end - ctx->header_name_start,

                           ctx->header_name_start,

                           ctx->header_end - ctx->header_start,

                           ctx->header_start);

            len = ctx->header_name_end - ctx->header_name_start;

            if (len == sizeof("Content-Type") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Content-Type",

                                   sizeof("Content-Type") - 1)

                   == 0)

            {

                len = ctx->header_end - ctx->header_start;

                if (len != sizeof("application/ocsp-response") - 1

                    || ngx_strncasecmp(ctx->header_start,

                                       (u_char *) "application/ocsp-response",

                                       sizeof("application/ocsp-response") - 1)

                       != 0)

                {

                    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                                  "OCSP responder sent invalid "

                                  "\"Content-Type\" header: \"%*s\"",

                                  ctx->header_end - ctx->header_start,

                                  ctx->header_start);

                    return NGX_ERROR;

                }

                continue;

            }

            /* TODO: honor Content-Length */

            continue;

        }

        if (rc == NGX_DONE) {

            break;

        }

        if (rc == NGX_AGAIN) {

            return NGX_AGAIN;

        }

        /* rc == NGX_ERROR */

        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,

                      "OCSP responder sent invalid response");

        return NGX_ERROR;

    }

    ctx->process = ngx_ssl_ocsp_process_body;

    return ctx->process(ctx);

}

static ngx_int_t

‌ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)

{

    u_char      c, ch, *p;

    enum {

        sw_start = 0,

        sw_name,

        sw_space_before_value,

        sw_value,

        sw_space_after_value,

        sw_almost_done,

        sw_header_almost_done

    } state;

    state = ctx->state;

    for (p = ctx->response->pos; p < ctx->response->last; p++) {

        ch = *p;

#if 0

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                       "s:%d in:'%02Xd:%c'", state, ch, ch);

#endif

        switch (state) {

        /* first char */

        case sw_start:

            switch (ch) {

            case CR:

                ctx->header_end = p;

                state = sw_header_almost_done;

                break;

            case LF:

                ctx->header_end = p;

                goto header_done;

            default:

                state = sw_name;

                ctx->header_name_start = p;

                c = (u_char) (ch | 0x20);

                if (c >= 'a' && c <= 'z') {

                    break;

                }

                if (ch >= '0' && ch <= '9') {

                    break;

                }

                return NGX_ERROR;

            }

            break;

        /* header name */

        case sw_name:

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                break;

            }

            if (ch == ':') {

                ctx->header_name_end = p;

                state = sw_space_before_value;

                break;

            }

            if (ch == '-') {

                break;

            }

            if (ch >= '0' && ch <= '9') {

                break;

            }

            if (ch == CR) {

                ctx->header_name_end = p;

                ctx->header_start = p;

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            }

            if (ch == LF) {

                ctx->header_name_end = p;

                ctx->header_start = p;

                ctx->header_end = p;

                goto done;

            }

            return NGX_ERROR;

        /* space* before header value */

        case sw_space_before_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                ctx->header_start = p;

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                ctx->header_start = p;

                ctx->header_end = p;

                goto done;

            default:

                ctx->header_start = p;

                state = sw_value;

                break;

            }

            break;

        /* header value */

        case sw_value:

            switch (ch) {

            case ' ':

                ctx->header_end = p;

                state = sw_space_after_value;

                break;

            case CR:

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                ctx->header_end = p;

                goto done;

            }

            break;

        /* space* before end of header line */

        case sw_space_after_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                state = sw_value;

                break;

            }

            break;

        /* end of header line */

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

        /* end of header */

        case sw_header_almost_done:

            switch (ch) {

            case LF:

                goto header_done;

            default:

                return NGX_ERROR;

            }

        }

    }

    ctx->response->pos = p;

    ctx->state = state;

    return NGX_AGAIN;

done:

    ctx->response->pos = p + 1;

    ctx->state = sw_start;

    return NGX_OK;

header_done:

    ctx->response->pos = p + 1;

    ctx->state = sw_start;

    return NGX_DONE;

}

static ngx_int_t

‌ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)

{

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,

                   "ssl ocsp process body");

    if (ctx->done) {

        ctx->handler(ctx);

        return NGX_DONE;

    }

    return NGX_AGAIN;

}

static u_char *

‌ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)

{

    u_char              *p;

    ngx_ssl_ocsp_ctx_t  *ctx;

    p = buf;

    if (log->action) {

        p = ngx_snprintf(buf, len, " while %s", log->action);

        len -= p - buf;

    }

    ctx = log->data;

    if (ctx) {

        p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);

    }

    return p;

}

#else

ngx_int_t

‌ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,

    ngx_str_t *responder, ngx_uint_t verify)

{

    ngx_log_error(NGX_LOG_WARN, ssl->log, 0,

                  "\"ssl_stapling\" ignored, not supported");

    return NGX_OK;

}

ngx_int_t

‌ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,

    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)

{

    return NGX_OK;

}

#endif

src/core/ngx_resolver.h - nginx-1.7.9

Data types defined

ngx_resolver_ctx_s

ngx_resolver_ctx_t

ngx_resolver_handler_pt

ngx_resolver_node_t

ngx_resolver_t

ngx_udp_connection_t

Macros defined

NGX_NO_RESOLVER

NGX_RESOLVER_MAX_RECURSION

NGX_RESOLVE_A

NGX_RESOLVE_AAAA

NGX_RESOLVE_CNAME

NGX_RESOLVE_DNAME

NGX_RESOLVE_FORMERR

NGX_RESOLVE_MX

NGX_RESOLVE_NOTIMP

NGX_RESOLVE_NXDOMAIN

NGX_RESOLVE_PTR

NGX_RESOLVE_REFUSED

NGX_RESOLVE_SERVFAIL

NGX_RESOLVE_TIMEDOUT

NGX_RESOLVE_TXT

_NGX_RESOLVER_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#ifndef _NGX_RESOLVER_H_INCLUDED_

‌#define _NGX_RESOLVER_H_INCLUDED_

‌#define NGX_RESOLVE_A         1

‌#define NGX_RESOLVE_CNAME     5

‌#define NGX_RESOLVE_PTR       12

‌#define NGX_RESOLVE_MX        15

‌#define NGX_RESOLVE_TXT       16

#if (NGX_HAVE_INET6)

‌#define NGX_RESOLVE_AAAA      28

#endif

‌#define NGX_RESOLVE_DNAME     39

‌#define NGX_RESOLVE_FORMERR   1

‌#define NGX_RESOLVE_SERVFAIL  2

‌#define NGX_RESOLVE_NXDOMAIN  3

‌#define NGX_RESOLVE_NOTIMP    4

‌#define NGX_RESOLVE_REFUSED   5

‌#define NGX_RESOLVE_TIMEDOUT  NGX_ETIMEDOUT

‌#define NGX_NO_RESOLVER       (void *) -1

‌#define NGX_RESOLVER_MAX_RECURSION    50

typedef struct {

    ngx_connection_t         *connection;

    struct sockaddr          *sockaddr;

    socklen_t                 socklen;

    ngx_str_t                 server;

    ngx_log_t                 log;

‌} ngx_udp_connection_t;

‌typedef struct ngx_resolver_ctx_s  ngx_resolver_ctx_t;

‌typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx);

typedef struct {

    /* PTR: resolved name, A: name to resolve */

    u_char                   *name;

    ngx_queue_t               queue;

    /* event ident must be after 3 pointers as in ngx_connection_t */

    ngx_int_t                 ident;

    ngx_rbtree_node_t         node;

#if (NGX_HAVE_INET6)

    /* PTR: IPv6 address to resolve (IPv4 address is in rbtree node key) */

    struct in6_addr           addr6;

#endif

    u_short                   nlen;

    u_short                   qlen;

    u_char                   *query;

#if (NGX_HAVE_INET6)

    u_char                   *query6;

#endif

    union {

        in_addr_t             addr;

        in_addr_t            *addrs;

        u_char               *cname;

    } u;

    u_char                    code;

    u_short                   naddrs;

    u_short                   cnlen;

#if (NGX_HAVE_INET6)

    union {

        struct in6_addr       addr6;

        struct in6_addr      *addrs6;

    } u6;

    u_short                   naddrs6;

#endif

    time_t                    expire;

    time_t                    valid;

    uint32_t                  ttl;

    ngx_resolver_ctx_t       *waiting;

‌} ngx_resolver_node_t;

typedef struct {

    /* has to be pointer because of "incomplete type" */

    ngx_event_t              *event;

    void                     *dummy;

    ngx_log_t                *log;

    /* event ident must be after 3 pointers as in ngx_connection_t */

    ngx_int_t                 ident;

    /* simple round robin DNS peers balancer */

    ngx_array_t               udp_connections;

    ngx_uint_t                last_connection;

    ngx_rbtree_t              name_rbtree;

    ngx_rbtree_node_t         name_sentinel;

    ngx_rbtree_t              addr_rbtree;

    ngx_rbtree_node_t         addr_sentinel;

    ngx_queue_t               name_resend_queue;

    ngx_queue_t               addr_resend_queue;

    ngx_queue_t               name_expire_queue;

    ngx_queue_t               addr_expire_queue;

#if (NGX_HAVE_INET6)

    ngx_uint_t                ipv6;                 /* unsigned  ipv6:1; */

    ngx_rbtree_t              addr6_rbtree;

    ngx_rbtree_node_t         addr6_sentinel;

    ngx_queue_t               addr6_resend_queue;

    ngx_queue_t               addr6_expire_queue;

#endif

    time_t                    resend_timeout;

    time_t                    expire;

    time_t                    valid;

    ngx_uint_t                log_level;

‌} ngx_resolver_t;

‌struct ngx_resolver_ctx_s {

    ngx_resolver_ctx_t       *next;

    ngx_resolver_t           *resolver;

    ngx_udp_connection_t     *udp_connection;

    ngx_int_t                 state;

    ngx_str_t                 name;

    ngx_uint_t                naddrs;

    ngx_addr_t               *addrs;

    ngx_addr_t                addr;

    struct sockaddr_in        sin;

    ngx_resolver_handler_pt   handler;

    void                     *data;

    ngx_msec_t                timeout;

    ngx_uint_t                quick;  /* unsigned  quick:1; */

    ngx_uint_t                recursion;

    ngx_event_t              *event;

};

ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names,

    ngx_uint_t n);

ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r,

    ngx_resolver_ctx_t *temp);

ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx);

void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx);

ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx);

void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx);

char *ngx_resolver_strerror(ngx_int_t err);

#endif /* _NGX_RESOLVER_H_INCLUDED_ */

src/core/ngx_resolver.c - nginx-1.7.9

Data types defined

ngx_resolver_an_t

ngx_resolver_hdr_t

ngx_resolver_qs_t

Functions defined

ngx_resolve_addr

ngx_resolve_addr_done

ngx_resolve_name

ngx_resolve_name_done

ngx_resolve_name_locked

ngx_resolve_start

ngx_resolver_alloc

ngx_resolver_calloc

ngx_resolver_cleanup

ngx_resolver_cleanup_tree

ngx_resolver_copy

ngx_resolver_create

ngx_resolver_create_addr_query

ngx_resolver_create_name_query

ngx_resolver_dup

ngx_resolver_expire

ngx_resolver_export

ngx_resolver_free

ngx_resolver_free_locked

ngx_resolver_free_node

ngx_resolver_log_error

ngx_resolver_lookup_addr

ngx_resolver_lookup_addr6

ngx_resolver_lookup_name

ngx_resolver_process_a

ngx_resolver_process_ptr

ngx_resolver_process_response

ngx_resolver_rbtree_insert_addr6_value

ngx_resolver_rbtree_insert_value

ngx_resolver_read_response

ngx_resolver_resend

ngx_resolver_resend_handler

ngx_resolver_send_query

ngx_resolver_strerror

ngx_resolver_timeout_handler

ngx_udp_connect

Macros defined

NGX_RESOLVER_UDP_SIZE

ngx_resolver_node

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define NGX_RESOLVER_UDP_SIZE   4096

typedef struct {

    u_char  ident_hi;

    u_char  ident_lo;

    u_char  flags_hi;

    u_char  flags_lo;

    u_char  nqs_hi;

    u_char  nqs_lo;

    u_char  nan_hi;

    u_char  nan_lo;

    u_char  nns_hi;

    u_char  nns_lo;

    u_char  nar_hi;

    u_char  nar_lo;

‌} ngx_resolver_hdr_t;

typedef struct {

    u_char  type_hi;

    u_char  type_lo;

    u_char  class_hi;

    u_char  class_lo;

‌} ngx_resolver_qs_t;

typedef struct {

    u_char  type_hi;

    u_char  type_lo;

    u_char  class_hi;

    u_char  class_lo;

    u_char  ttl[4];

    u_char  len_hi;

    u_char  len_lo;

‌} ngx_resolver_an_t;

‌#define ngx_resolver_node(n)                                                 \

    (ngx_resolver_node_t *)                                                  \

        ((u_char *) (n) - offsetof(ngx_resolver_node_t, node))

ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc);

static void ngx_resolver_cleanup(void *data);

static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);

static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,

    ngx_resolver_ctx_t *ctx);

static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,

    ngx_queue_t *queue);

static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,

    ngx_resolver_node_t *rn);

static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn,

    ngx_resolver_ctx_t *ctx);

static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn,

    ngx_resolver_ctx_t *ctx);

static void ngx_resolver_resend_handler(ngx_event_t *ev);

static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree,

    ngx_queue_t *queue);

static void ngx_resolver_read_response(ngx_event_t *rev);

static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,

    size_t n);

static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,

    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype,

    ngx_uint_t nan, ngx_uint_t ans);

static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,

    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);

static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,

    ngx_str_t *name, uint32_t hash);

static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,

    in_addr_t addr);

static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,

    u_char *buf, u_char *src, u_char *last);

static void ngx_resolver_timeout_handler(ngx_event_t *ev);

static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn);

static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size);

static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);

static void ngx_resolver_free(ngx_resolver_t *r, void *p);

static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);

static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);

static ngx_addr_t *ngx_resolver_export(ngx_resolver_t *r,

    ngx_resolver_node_t *rn, ngx_uint_t rotate);

static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);

#if (NGX_HAVE_INET6)

static void ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r,

    struct in6_addr *addr, uint32_t hash);

#endif

ngx_resolver_t *

‌ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n)

{

    ngx_str_t              s;

    ngx_url_t              u;

    ngx_uint_t             i, j;

    ngx_resolver_t        *r;

    ngx_pool_cleanup_t    *cln;

    ngx_udp_connection_t  *uc;

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NULL;

    }

    cln->handler = ngx_resolver_cleanup;

    r = ngx_calloc(sizeof(ngx_resolver_t), cf->log);

    if (r == NULL) {

        return NULL;

    }

    cln->data = r;

    r->event = ngx_calloc(sizeof(ngx_event_t), cf->log);

    if (r->event == NULL) {

        return NULL;

    }

    ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,

                    ngx_resolver_rbtree_insert_value);

    ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,

                    ngx_rbtree_insert_value);

    ngx_queue_init(&r->name_resend_queue);

    ngx_queue_init(&r->addr_resend_queue);

    ngx_queue_init(&r->name_expire_queue);

    ngx_queue_init(&r->addr_expire_queue);

#if (NGX_HAVE_INET6)

    r->ipv6 = 1;

    ngx_rbtree_init(&r->addr6_rbtree, &r->addr6_sentinel,

                    ngx_resolver_rbtree_insert_addr6_value);

    ngx_queue_init(&r->addr6_resend_queue);

    ngx_queue_init(&r->addr6_expire_queue);

#endif

    r->event->handler = ngx_resolver_resend_handler;

    r->event->data = r;

    r->event->log = &cf->cycle->new_log;

    r->ident = -1;

    r->resend_timeout = 5;

    r->expire = 30;

    r->valid = 0;

    r->log = &cf->cycle->new_log;

    r->log_level = NGX_LOG_ERR;

    if (n) {

        if (ngx_array_init(&r->udp_connections, cf->pool, n,

                           sizeof(ngx_udp_connection_t))

            != NGX_OK)

        {

            return NULL;

        }

    }

    for (i = 0; i < n; i++) {

        if (ngx_strncmp(names[i].data, "valid=", 6) == 0) {

            s.len = names[i].len - 6;

            s.data = names[i].data + 6;

            r->valid = ngx_parse_time(&s, 1);

            if (r->valid == (time_t) NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid parameter: %V", &names[i]);

                return NULL;

            }

            continue;

        }

#if (NGX_HAVE_INET6)

        if (ngx_strncmp(names[i].data, "ipv6=", 5) == 0) {

            if (ngx_strcmp(&names[i].data[5], "on") == 0) {

                r->ipv6 = 1;

            } else if (ngx_strcmp(&names[i].data[5], "off") == 0) {

                r->ipv6 = 0;

            } else {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid parameter: %V", &names[i]);

                return NULL;

            }

            continue;

        }

#endif

        ngx_memzero(&u, sizeof(ngx_url_t));

        u.url = names[i];

        u.default_port = 53;

        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

            if (u.err) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "%s in resolver \"%V\"",

                                   u.err, &u.url);

            }

            return NULL;

        }

        uc = ngx_array_push_n(&r->udp_connections, u.naddrs);

        if (uc == NULL) {

            return NULL;

        }

        ngx_memzero(uc, u.naddrs * sizeof(ngx_udp_connection_t));

        for (j = 0; j < u.naddrs; j++) {

            uc[j].sockaddr = u.addrs[j].sockaddr;

            uc[j].socklen = u.addrs[j].socklen;

            uc[j].server = u.addrs[j].name;

        }

    }

    return r;

}

static void

‌ngx_resolver_cleanup(void *data)

{

    ngx_resolver_t  *r = data;

    ngx_uint_t             i;

    ngx_udp_connection_t  *uc;

    if (r) {

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                       "cleanup resolver");

        ngx_resolver_cleanup_tree(r, &r->name_rbtree);

        ngx_resolver_cleanup_tree(r, &r->addr_rbtree);

#if (NGX_HAVE_INET6)

        ngx_resolver_cleanup_tree(r, &r->addr6_rbtree);

#endif

        if (r->event) {

            ngx_free(r->event);

        }

        uc = r->udp_connections.elts;

        for (i = 0; i < r->udp_connections.nelts; i++) {

            if (uc[i].connection) {

                ngx_close_connection(uc[i].connection);

            }

        }

        ngx_free(r);

    }

}

static void

‌ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)

{

    ngx_resolver_ctx_t   *ctx, *next;

    ngx_resolver_node_t  *rn;

    while (tree->root != tree->sentinel) {

        rn = ngx_resolver_node(ngx_rbtree_min(tree->root, tree->sentinel));

        ngx_queue_remove(&rn->queue);

        for (ctx = rn->waiting; ctx; ctx = next) {

            next = ctx->next;

            if (ctx->event) {

                ngx_resolver_free(r, ctx->event);

            }

            ngx_resolver_free(r, ctx);

        }

        ngx_rbtree_delete(tree, &rn->node);

        ngx_resolver_free_node(r, rn);

    }

}

ngx_resolver_ctx_t *

‌ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)

{

    in_addr_t            addr;

    ngx_resolver_ctx_t  *ctx;

    if (temp) {

        addr = ngx_inet_addr(temp->name.data, temp->name.len);

        if (addr != INADDR_NONE) {

            temp->resolver = r;

            temp->state = NGX_OK;

            temp->naddrs = 1;

            temp->addrs = &temp->addr;

            temp->addr.sockaddr = (struct sockaddr *) &temp->sin;

            temp->addr.socklen = sizeof(struct sockaddr_in);

            ngx_memzero(&temp->sin, sizeof(struct sockaddr_in));

            temp->sin.sin_family = AF_INET;

            temp->sin.sin_addr.s_addr = addr;

            temp->quick = 1;

            return temp;

        }

    }

    if (r->udp_connections.nelts == 0) {

        return NGX_NO_RESOLVER;

    }

    ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t));

    if (ctx) {

        ctx->resolver = r;

    }

    return ctx;

}

ngx_int_t

‌ngx_resolve_name(ngx_resolver_ctx_t *ctx)

{

    ngx_int_t        rc;

    ngx_resolver_t  *r;

    r = ctx->resolver;

    if (ctx->name.len > 0 && ctx->name.data[ctx->name.len - 1] == '.') {

        ctx->name.len--;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolve: \"%V\"", &ctx->name);

    if (ctx->quick) {

        ctx->handler(ctx);

        return NGX_OK;

    }

    /* lock name mutex */

    rc = ngx_resolve_name_locked(r, ctx);

    if (rc == NGX_OK) {

        return NGX_OK;

    }

    /* unlock name mutex */

    if (rc == NGX_AGAIN) {

        return NGX_OK;

    }

    /* NGX_ERROR */

    if (ctx->event) {

        ngx_resolver_free(r, ctx->event);

    }

    ngx_resolver_free(r, ctx);

    return NGX_ERROR;

}

void

‌ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)

{

    uint32_t              hash;

    ngx_resolver_t       *r;

    ngx_resolver_ctx_t   *w, **p;

    ngx_resolver_node_t  *rn;

    r = ctx->resolver;

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolve name done: %i", ctx->state);

    if (ctx->quick) {

        return;

    }

    if (ctx->event && ctx->event->timer_set) {

        ngx_del_timer(ctx->event);

    }

    /* lock name mutex */

    if (ctx->state == NGX_AGAIN) {

        hash = ngx_crc32_short(ctx->name.data, ctx->name.len);

        rn = ngx_resolver_lookup_name(r, &ctx->name, hash);

        if (rn) {

            p = &rn->waiting;

            w = rn->waiting;

            while (w) {

                if (w == ctx) {

                    *p = w->next;

                    goto done;

                }

                p = &w->next;

                w = w->next;

            }

        }

        ngx_log_error(NGX_LOG_ALERT, r->log, 0,

                      "could not cancel %V resolving", &ctx->name);

    }

done:

    ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue);

    /* unlock name mutex */

    /* lock alloc mutex */

    if (ctx->event) {

        ngx_resolver_free_locked(r, ctx->event);

    }

    ngx_resolver_free_locked(r, ctx);

    /* unlock alloc mutex */

}

static ngx_int_t

‌ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)

{

    uint32_t              hash;

    ngx_int_t             rc;

    ngx_uint_t            naddrs;

    ngx_addr_t           *addrs;

    ngx_resolver_ctx_t   *next;

    ngx_resolver_node_t  *rn;

    ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len);

    hash = ngx_crc32_short(ctx->name.data, ctx->name.len);

    rn = ngx_resolver_lookup_name(r, &ctx->name, hash);

    if (rn) {

        if (rn->valid >= ngx_time()) {

            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");

            ngx_queue_remove(&rn->queue);

            rn->expire = ngx_time() + r->expire;

            ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);

            naddrs = (rn->naddrs == (u_short) -1) ? 0 : rn->naddrs;

#if (NGX_HAVE_INET6)

            naddrs += (rn->naddrs6 == (u_short) -1) ? 0 : rn->naddrs6;

#endif

            if (naddrs) {

                if (naddrs == 1 && rn->naddrs == 1) {

                    addrs = NULL;

                } else {

                    addrs = ngx_resolver_export(r, rn, 1);

                    if (addrs == NULL) {

                        return NGX_ERROR;

                    }

                }

                ctx->next = rn->waiting;

                rn->waiting = NULL;

                /* unlock name mutex */

                do {

                    ctx->state = NGX_OK;

                    ctx->naddrs = naddrs;

                    if (addrs == NULL) {

                        ctx->addrs = &ctx->addr;

                        ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;

                        ctx->addr.socklen = sizeof(struct sockaddr_in);

                        ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));

                        ctx->sin.sin_family = AF_INET;

                        ctx->sin.sin_addr.s_addr = rn->u.addr;

                    } else {

                        ctx->addrs = addrs;

                    }

                    next = ctx->next;

                    ctx->handler(ctx);

                    ctx = next;

                } while (ctx);

                if (addrs != NULL) {

                    ngx_resolver_free(r, addrs->sockaddr);

                    ngx_resolver_free(r, addrs);

                }

                return NGX_OK;

            }

            /* NGX_RESOLVE_CNAME */

            if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {

                ctx->name.len = rn->cnlen;

                ctx->name.data = rn->u.cname;

                return ngx_resolve_name_locked(r, ctx);

            }

            ctx->next = rn->waiting;

            rn->waiting = NULL;

            /* unlock name mutex */

            do {

                ctx->state = NGX_RESOLVE_NXDOMAIN;

                next = ctx->next;

                ctx->handler(ctx);

                ctx = next;

            } while (ctx);

            return NGX_OK;

        }

        if (rn->waiting) {

            ctx->next = rn->waiting;

            rn->waiting = ctx;

            ctx->state = NGX_AGAIN;

            return NGX_AGAIN;

        }

        ngx_queue_remove(&rn->queue);

        /* lock alloc mutex */

        if (rn->query) {

            ngx_resolver_free_locked(r, rn->query);

            rn->query = NULL;

#if (NGX_HAVE_INET6)

            rn->query6 = NULL;

#endif

        }

        if (rn->cnlen) {

            ngx_resolver_free_locked(r, rn->u.cname);

        }

        if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) {

            ngx_resolver_free_locked(r, rn->u.addrs);

        }

#if (NGX_HAVE_INET6)

        if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {

            ngx_resolver_free_locked(r, rn->u6.addrs6);

        }

#endif

        /* unlock alloc mutex */

    } else {

        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));

        if (rn == NULL) {

            return NGX_ERROR;

        }

        rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len);

        if (rn->name == NULL) {

            ngx_resolver_free(r, rn);

            return NGX_ERROR;

        }

        rn->node.key = hash;

        rn->nlen = (u_short) ctx->name.len;

        rn->query = NULL;

#if (NGX_HAVE_INET6)

        rn->query6 = NULL;

#endif

        ngx_rbtree_insert(&r->name_rbtree, &rn->node);

    }

    rc = ngx_resolver_create_name_query(rn, ctx);

    if (rc == NGX_ERROR) {

        goto failed;

    }

    if (rc == NGX_DECLINED) {

        ngx_rbtree_delete(&r->name_rbtree, &rn->node);

        ngx_resolver_free(r, rn->query);

        ngx_resolver_free(r, rn->name);

        ngx_resolver_free(r, rn);

        ctx->state = NGX_RESOLVE_NXDOMAIN;

        ctx->handler(ctx);

        return NGX_OK;

    }

    rn->naddrs = (u_short) -1;

#if (NGX_HAVE_INET6)

    rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0;

#endif

    if (ngx_resolver_send_query(r, rn) != NGX_OK) {

        goto failed;

    }

    if (ctx->event == NULL) {

        ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));

        if (ctx->event == NULL) {

            goto failed;

        }

        ctx->event->handler = ngx_resolver_timeout_handler;

        ctx->event->data = rn;

        ctx->event->log = r->log;

        rn->ident = -1;

        ngx_add_timer(ctx->event, ctx->timeout);

    }

    if (ngx_queue_empty(&r->name_resend_queue)) {

        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));

    }

    rn->expire = ngx_time() + r->resend_timeout;

    ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);

    rn->code = 0;

    rn->cnlen = 0;

    rn->valid = 0;

    rn->ttl = NGX_MAX_UINT32_VALUE;

    rn->waiting = ctx;

    ctx->state = NGX_AGAIN;

    return NGX_AGAIN;

failed:

    ngx_rbtree_delete(&r->name_rbtree, &rn->node);

    if (rn->query) {

        ngx_resolver_free(r, rn->query);

    }

    ngx_resolver_free(r, rn->name);

    ngx_resolver_free(r, rn);

    return NGX_ERROR;

}

ngx_int_t

‌ngx_resolve_addr(ngx_resolver_ctx_t *ctx)

{

    u_char               *name;

    in_addr_t             addr;

    ngx_queue_t          *resend_queue, *expire_queue;

    ngx_rbtree_t         *tree;

    ngx_resolver_t       *r;

    struct sockaddr_in   *sin;

    ngx_resolver_node_t  *rn;

#if (NGX_HAVE_INET6)

    uint32_t              hash;

    struct sockaddr_in6  *sin6;

#endif

#if (NGX_SUPPRESS_WARN)

    addr = 0;

#if (NGX_HAVE_INET6)

    hash = 0;

    sin6 = NULL;

#endif

#endif

    r = ctx->resolver;

    switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;

        hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);

        /* lock addr mutex */

        rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);

        tree = &r->addr6_rbtree;

        resend_queue = &r->addr6_resend_queue;

        expire_queue = &r->addr6_expire_queue;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) ctx->addr.sockaddr;

        addr = ntohl(sin->sin_addr.s_addr);

        /* lock addr mutex */

        rn = ngx_resolver_lookup_addr(r, addr);

        tree = &r->addr_rbtree;

        resend_queue = &r->addr_resend_queue;

        expire_queue = &r->addr_expire_queue;

    }

    if (rn) {

        if (rn->valid >= ngx_time()) {

            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");

            ngx_queue_remove(&rn->queue);

            rn->expire = ngx_time() + r->expire;

            ngx_queue_insert_head(expire_queue, &rn->queue);

            name = ngx_resolver_dup(r, rn->name, rn->nlen);

            if (name == NULL) {

                goto failed;

            }

            ctx->name.len = rn->nlen;

            ctx->name.data = name;

            /* unlock addr mutex */

            ctx->state = NGX_OK;

            ctx->handler(ctx);

            ngx_resolver_free(r, name);

            return NGX_OK;

        }

        if (rn->waiting) {

            ctx->next = rn->waiting;

            rn->waiting = ctx;

            ctx->state = NGX_AGAIN;

            /* unlock addr mutex */

            return NGX_OK;

        }

        ngx_queue_remove(&rn->queue);

        ngx_resolver_free(r, rn->query);

        rn->query = NULL;

#if (NGX_HAVE_INET6)

        rn->query6 = NULL;

#endif

    } else {

        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));

        if (rn == NULL) {

            goto failed;

        }

        switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            rn->addr6 = sin6->sin6_addr;

            rn->node.key = hash;

            break;

#endif

        default: /* AF_INET */

            rn->node.key = addr;

        }

        rn->query = NULL;

#if (NGX_HAVE_INET6)

        rn->query6 = NULL;

#endif

        ngx_rbtree_insert(tree, &rn->node);

    }

    if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) {

        goto failed;

    }

    rn->naddrs = (u_short) -1;

#if (NGX_HAVE_INET6)

    rn->naddrs6 = (u_short) -1;

#endif

    if (ngx_resolver_send_query(r, rn) != NGX_OK) {

        goto failed;

    }

    ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));

    if (ctx->event == NULL) {

        goto failed;

    }

    ctx->event->handler = ngx_resolver_timeout_handler;

    ctx->event->data = rn;

    ctx->event->log = r->log;

    rn->ident = -1;

    ngx_add_timer(ctx->event, ctx->timeout);

    if (ngx_queue_empty(resend_queue)) {

        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));

    }

    rn->expire = ngx_time() + r->resend_timeout;

    ngx_queue_insert_head(resend_queue, &rn->queue);

    rn->code = 0;

    rn->cnlen = 0;

    rn->name = NULL;

    rn->nlen = 0;

    rn->valid = 0;

    rn->ttl = NGX_MAX_UINT32_VALUE;

    rn->waiting = ctx;

    /* unlock addr mutex */

    ctx->state = NGX_AGAIN;

    return NGX_OK;

failed:

    if (rn) {

        ngx_rbtree_delete(tree, &rn->node);

        if (rn->query) {

            ngx_resolver_free(r, rn->query);

        }

        ngx_resolver_free(r, rn);

    }

    /* unlock addr mutex */

    if (ctx->event) {

        ngx_resolver_free(r, ctx->event);

    }

    ngx_resolver_free(r, ctx);

    return NGX_ERROR;

}

void

‌ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)

{

    in_addr_t             addr;

    ngx_queue_t          *expire_queue;

    ngx_rbtree_t         *tree;

    ngx_resolver_t       *r;

    ngx_resolver_ctx_t   *w, **p;

    struct sockaddr_in   *sin;

    ngx_resolver_node_t  *rn;

#if (NGX_HAVE_INET6)

    uint32_t              hash;

    struct sockaddr_in6  *sin6;

#endif

    r = ctx->resolver;

    switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        tree = &r->addr6_rbtree;

        expire_queue = &r->addr6_expire_queue;

        break;

#endif

    default: /* AF_INET */

        tree = &r->addr_rbtree;

        expire_queue = &r->addr_expire_queue;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolve addr done: %i", ctx->state);

    if (ctx->event && ctx->event->timer_set) {

        ngx_del_timer(ctx->event);

    }

    /* lock addr mutex */

    if (ctx->state == NGX_AGAIN) {

        switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;

            hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);

            rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) ctx->addr.sockaddr;

            addr = ntohl(sin->sin_addr.s_addr);

            rn = ngx_resolver_lookup_addr(r, addr);

        }

        if (rn) {

            p = &rn->waiting;

            w = rn->waiting;

            while (w) {

                if (w == ctx) {

                    *p = w->next;

                    goto done;

                }

                p = &w->next;

                w = w->next;

            }

        }

        {

            u_char     text[NGX_SOCKADDR_STRLEN];

            ngx_str_t  addrtext;

            addrtext.data = text;

            addrtext.len = ngx_sock_ntop(ctx->addr.sockaddr, ctx->addr.socklen,

                                         text, NGX_SOCKADDR_STRLEN, 0);

            ngx_log_error(NGX_LOG_ALERT, r->log, 0,

                          "could not cancel %V resolving", &addrtext);

        }

    }

done:

    ngx_resolver_expire(r, tree, expire_queue);

    /* unlock addr mutex */

    /* lock alloc mutex */

    if (ctx->event) {

        ngx_resolver_free_locked(r, ctx->event);

    }

    ngx_resolver_free_locked(r, ctx);

    /* unlock alloc mutex */

}

static void

‌ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)

{

    time_t                now;

    ngx_uint_t            i;

    ngx_queue_t          *q;

    ngx_resolver_node_t  *rn;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire");

    now = ngx_time();

    for (i = 0; i < 2; i++) {

        if (ngx_queue_empty(queue)) {

            return;

        }

        q = ngx_queue_last(queue);

        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);

        if (now <= rn->expire) {

            return;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,

                       "resolver expire \"%*s\"", (size_t) rn->nlen, rn->name);

        ngx_queue_remove(q);

        ngx_rbtree_delete(tree, &rn->node);

        ngx_resolver_free_node(r, rn);

    }

}

static ngx_int_t

‌ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)

{

    ssize_t                n;

    ngx_udp_connection_t  *uc;

    uc = r->udp_connections.elts;

    uc = &uc[r->last_connection++];

    if (r->last_connection == r->udp_connections.nelts) {

        r->last_connection = 0;

    }

    if (uc->connection == NULL) {

        uc->log = *r->log;

        uc->log.handler = ngx_resolver_log_error;

        uc->log.data = uc;

        uc->log.action = "resolving";

        if (ngx_udp_connect(uc) != NGX_OK) {

            return NGX_ERROR;

        }

        uc->connection->data = r;

        uc->connection->read->handler = ngx_resolver_read_response;

        uc->connection->read->resolver = 1;

    }

    if (rn->naddrs == (u_short) -1) {

        n = ngx_send(uc->connection, rn->query, rn->qlen);

        if (n == -1) {

            return NGX_ERROR;

        }

        if ((size_t) n != (size_t) rn->qlen) {

            ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");

            return NGX_ERROR;

        }

    }

#if (NGX_HAVE_INET6)

    if (rn->query6 && rn->naddrs6 == (u_short) -1) {

        n = ngx_send(uc->connection, rn->query6, rn->qlen);

        if (n == -1) {

            return NGX_ERROR;

        }

        if ((size_t) n != (size_t) rn->qlen) {

            ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");

            return NGX_ERROR;

        }

    }

#endif

    return NGX_OK;

}

static void

‌ngx_resolver_resend_handler(ngx_event_t *ev)

{

    time_t           timer, atimer, ntimer;

#if (NGX_HAVE_INET6)

    time_t           a6timer;

#endif

    ngx_resolver_t  *r;

    r = ev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolver resend handler");

    /* lock name mutex */

    ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue);

    /* unlock name mutex */

    /* lock addr mutex */

    atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue);

    /* unlock addr mutex */

#if (NGX_HAVE_INET6)

    /* lock addr6 mutex */

    a6timer = ngx_resolver_resend(r, &r->addr6_rbtree, &r->addr6_resend_queue);

    /* unlock addr6 mutex */

#endif

    timer = ntimer;

    if (timer == 0) {

        timer = atimer;

    } else if (atimer) {

        timer = ngx_min(timer, atimer);

    }

#if (NGX_HAVE_INET6)

    if (timer == 0) {

        timer = a6timer;

    } else if (a6timer) {

        timer = ngx_min(timer, a6timer);

    }

#endif

    if (timer) {

        ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000));

    }

}

static time_t

‌ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)

{

    time_t                now;

    ngx_queue_t          *q;

    ngx_resolver_node_t  *rn;

    now = ngx_time();

    for ( ;; ) {

        if (ngx_queue_empty(queue)) {

            return 0;

        }

        q = ngx_queue_last(queue);

        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);

        if (now < rn->expire) {

            return rn->expire - now;

        }

        ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,

                       "resolver resend \"%*s\" %p",

                       (size_t) rn->nlen, rn->name, rn->waiting);

        ngx_queue_remove(q);

        if (rn->waiting) {

            (void) ngx_resolver_send_query(r, rn);

            rn->expire = now + r->resend_timeout;

            ngx_queue_insert_head(queue, q);

            continue;

        }

        ngx_rbtree_delete(tree, &rn->node);

        ngx_resolver_free_node(r, rn);

    }

}

static void

‌ngx_resolver_read_response(ngx_event_t *rev)

{

    ssize_t            n;

    ngx_connection_t  *c;

    u_char             buf[NGX_RESOLVER_UDP_SIZE];

    c = rev->data;

    do {

        n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE);

        if (n < 0) {

            return;

        }

        ngx_resolver_process_response(c->data, buf, n);

    } while (rev->ready);

}

static void

‌ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n)

{

    char                 *err;

    ngx_uint_t            i, times, ident, qident, flags, code, nqs, nan,

                          qtype, qclass;

#if (NGX_HAVE_INET6)

    ngx_uint_t            qident6;

#endif

    ngx_queue_t          *q;

    ngx_resolver_qs_t    *qs;

    ngx_resolver_hdr_t   *response;

    ngx_resolver_node_t  *rn;

    if (n < sizeof(ngx_resolver_hdr_t)) {

        goto short_response;

    }

    response = (ngx_resolver_hdr_t *) buf;

    ident = (response->ident_hi << 8) + response->ident_lo;

    flags = (response->flags_hi << 8) + response->flags_lo;

    nqs = (response->nqs_hi << 8) + response->nqs_lo;

    nan = (response->nan_hi << 8) + response->nan_lo;

    ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolver DNS response %ui fl:%04Xui %ui/%ui/%ud/%ud",

                   ident, flags, nqs, nan,

                   (response->nns_hi << 8) + response->nns_lo,

                   (response->nar_hi << 8) + response->nar_lo);

    /* response to a standard query */

    if ((flags & 0xf870) != 0x8000) {

        ngx_log_error(r->log_level, r->log, 0,

                      "invalid DNS response %ui fl:%04Xui", ident, flags);

        return;

    }

    code = flags & 0xf;

    if (code == NGX_RESOLVE_FORMERR) {

        times = 0;

        for (q = ngx_queue_head(&r->name_resend_queue);

             q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100;

             q = ngx_queue_next(q))

        {

            rn = ngx_queue_data(q, ngx_resolver_node_t, queue);

            qident = (rn->query[0] << 8) + rn->query[1];

            if (qident == ident) {

                goto dns_error_name;

            }

#if (NGX_HAVE_INET6)

            if (rn->query6) {

                qident6 = (rn->query6[0] << 8) + rn->query6[1];

                if (qident6 == ident) {

                    goto dns_error_name;

                }

            }

#endif

        }

        goto dns_error;

    }

    if (code > NGX_RESOLVE_REFUSED) {

        goto dns_error;

    }

    if (nqs != 1) {

        err = "invalid number of questions in DNS response";

        goto done;

    }

    i = sizeof(ngx_resolver_hdr_t);

    while (i < (ngx_uint_t) n) {

        if (buf[i] == '\0') {

            goto found;

        }

        i += 1 + buf[i];

    }

    goto short_response;

found:

    if (i++ == sizeof(ngx_resolver_hdr_t)) {

        err = "zero-length domain name in DNS response";

        goto done;

    }

    if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t))

        > (ngx_uint_t) n)

    {

        goto short_response;

    }

    qs = (ngx_resolver_qs_t *) &buf[i];

    qtype = (qs->type_hi << 8) + qs->type_lo;

    qclass = (qs->class_hi << 8) + qs->class_lo;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolver DNS response qt:%ui cl:%ui", qtype, qclass);

    if (qclass != 1) {

        ngx_log_error(r->log_level, r->log, 0,

                      "unknown query class %ui in DNS response", qclass);

        return;

    }

    switch (qtype) {

    case NGX_RESOLVE_A:

#if (NGX_HAVE_INET6)

    case NGX_RESOLVE_AAAA:

#endif

        ngx_resolver_process_a(r, buf, n, ident, code, qtype, nan,

                               i + sizeof(ngx_resolver_qs_t));

        break;

    case NGX_RESOLVE_PTR:

        ngx_resolver_process_ptr(r, buf, n, ident, code, nan);

        break;

    default:

        ngx_log_error(r->log_level, r->log, 0,

                      "unknown query type %ui in DNS response", qtype);

        return;

    }

    return;

short_response:

    err = "short DNS response";

done:

    ngx_log_error(r->log_level, r->log, 0, err);

    return;

dns_error_name:

    ngx_log_error(r->log_level, r->log, 0,

                  "DNS error (%ui: %s), query id:%ui, name:\"%*s\"",

                  code, ngx_resolver_strerror(code), ident,

                  rn->nlen, rn->name);

    return;

dns_error:

    ngx_log_error(r->log_level, r->log, 0,

                  "DNS error (%ui: %s), query id:%ui",

                  code, ngx_resolver_strerror(code), ident);

    return;

}

static void

‌ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,

    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype,

    ngx_uint_t nan, ngx_uint_t ans)

{

    char                 *err;

    u_char               *cname;

    size_t                len;

    int32_t               ttl;

    uint32_t              hash;

    in_addr_t            *addr;

    ngx_str_t             name;

    ngx_addr_t           *addrs;

    ngx_uint_t            type, class, qident, naddrs, a, i, n, start;

#if (NGX_HAVE_INET6)

    struct in6_addr      *addr6;

#endif

    ngx_resolver_an_t    *an;

    ngx_resolver_ctx_t   *ctx, *next;

    ngx_resolver_node_t  *rn;

    if (ngx_resolver_copy(r, &name, buf,

                          buf + sizeof(ngx_resolver_hdr_t), buf + last)

        != NGX_OK)

    {

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name);

    hash = ngx_crc32_short(name.data, name.len);

    /* lock name mutex */

    rn = ngx_resolver_lookup_name(r, &name, hash);

    if (rn == NULL) {

        ngx_log_error(r->log_level, r->log, 0,

                      "unexpected response for %V", &name);

        ngx_resolver_free(r, name.data);

        goto failed;

    }

    switch (qtype) {

#if (NGX_HAVE_INET6)

    case NGX_RESOLVE_AAAA:

        if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) {

            ngx_log_error(r->log_level, r->log, 0,

                          "unexpected response for %V", &name);

            ngx_resolver_free(r, name.data);

            goto failed;

        }

        qident = (rn->query6[0] << 8) + rn->query6[1];

        break;

#endif

    default: /* NGX_RESOLVE_A */

        if (rn->query == NULL || rn->naddrs != (u_short) -1) {

            ngx_log_error(r->log_level, r->log, 0,

                          "unexpected response for %V", &name);

            ngx_resolver_free(r, name.data);

            goto failed;

        }

        qident = (rn->query[0] << 8) + rn->query[1];

    }

    if (ident != qident) {

        ngx_log_error(r->log_level, r->log, 0,

                      "wrong ident %ui response for %V, expect %ui",

                      ident, &name, qident);

        ngx_resolver_free(r, name.data);

        goto failed;

    }

    ngx_resolver_free(r, name.data);

    if (code == 0 && rn->code) {

        code = rn->code;

    }

    if (code == 0 && nan == 0) {

#if (NGX_HAVE_INET6)

        switch (qtype) {

        case NGX_RESOLVE_AAAA:

            rn->naddrs6 = 0;

            if (rn->naddrs == (u_short) -1) {

                goto next;

            }

            if (rn->naddrs) {

                goto export;

            }

            break;

        default: /* NGX_RESOLVE_A */

            rn->naddrs = 0;

            if (rn->naddrs6 == (u_short) -1) {

                goto next;

            }

            if (rn->naddrs6) {

                goto export;

            }

        }

#endif

        code = NGX_RESOLVE_NXDOMAIN;

    }

    if (code) {

#if (NGX_HAVE_INET6)

        switch (qtype) {

        case NGX_RESOLVE_AAAA:

            rn->naddrs6 = 0;

            if (rn->naddrs == (u_short) -1) {

                rn->code = (u_char) code;

                goto next;

            }

            break;

        default: /* NGX_RESOLVE_A */

            rn->naddrs = 0;

            if (rn->naddrs6 == (u_short) -1) {

                rn->code = (u_char) code;

                goto next;

            }

        }

#endif

        next = rn->waiting;

        rn->waiting = NULL;

        ngx_queue_remove(&rn->queue);

        ngx_rbtree_delete(&r->name_rbtree, &rn->node);

        /* unlock name mutex */

        while (next) {

            ctx = next;

            ctx->state = code;

            next = ctx->next;

            ctx->handler(ctx);

        }

        ngx_resolver_free_node(r, rn);

        return;

    }

    i = ans;

    naddrs = 0;

    cname = NULL;

    for (a = 0; a < nan; a++) {

        start = i;

        while (i < last) {

            if (buf[i] & 0xc0) {

                i += 2;

                goto found;

            }

            if (buf[i] == 0) {

                i++;

                goto test_length;

            }

            i += 1 + buf[i];

        }

        goto short_response;

    test_length:

        if (i - start < 2) {

            err = "invalid name in DNS response";

            goto invalid;

        }

    found:

        if (i + sizeof(ngx_resolver_an_t) >= last) {

            goto short_response;

        }

        an = (ngx_resolver_an_t *) &buf[i];

        type = (an->type_hi << 8) + an->type_lo;

        class = (an->class_hi << 8) + an->class_lo;

        len = (an->len_hi << 8) + an->len_lo;

        ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16)

            + (an->ttl[2] << 8) + (an->ttl[3]);

        if (class != 1) {

            ngx_log_error(r->log_level, r->log, 0,

                          "unexpected RR class %ui", class);

            goto failed;

        }

        if (ttl < 0) {

            ttl = 0;

        }

        rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl);

        i += sizeof(ngx_resolver_an_t);

        switch (type) {

        case NGX_RESOLVE_A:

            if (qtype != NGX_RESOLVE_A) {

                err = "unexpected A record in DNS response";

                goto invalid;

            }

            if (len != 4) {

                err = "invalid A record in DNS response";

                goto invalid;

            }

            if (i + 4 > last) {

                goto short_response;

            }

            naddrs++;

            break;

#if (NGX_HAVE_INET6)

        case NGX_RESOLVE_AAAA:

            if (qtype != NGX_RESOLVE_AAAA) {

                err = "unexpected AAAA record in DNS response";

                goto invalid;

            }

            if (len != 16) {

                err = "invalid AAAA record in DNS response";

                goto invalid;

            }

            if (i + 16 > last) {

                goto short_response;

            }

            naddrs++;

            break;

#endif

        case NGX_RESOLVE_CNAME:

            cname = &buf[i];

            break;

        case NGX_RESOLVE_DNAME:

            break;

        default:

            ngx_log_error(r->log_level, r->log, 0,

                          "unexpected RR type %ui", type);

        }

        i += len;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,

                   "resolver naddrs:%ui cname:%p ttl:%uD",

                   naddrs, cname, rn->ttl);

    if (naddrs) {

        switch (qtype) {

#if (NGX_HAVE_INET6)

        case NGX_RESOLVE_AAAA:

            if (naddrs == 1) {

                addr6 = &rn->u6.addr6;

                rn->naddrs6 = 1;

            } else {

                addr6 = ngx_resolver_alloc(r, naddrs * sizeof(struct in6_addr));

                if (addr6 == NULL) {

                    goto failed;

                }

                rn->u6.addrs6 = addr6;

                rn->naddrs6 = (u_short) naddrs;

            }

#if (NGX_SUPPRESS_WARN)

            addr = NULL;

#endif

            break;

#endif

        default: /* NGX_RESOLVE_A */

            if (naddrs == 1) {

                addr = &rn->u.addr;

                rn->naddrs = 1;

            } else {

                addr = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));

                if (addr == NULL) {

                    goto failed;

                }

                rn->u.addrs = addr;

                rn->naddrs = (u_short) naddrs;

            }

#if (NGX_HAVE_INET6 && NGX_SUPPRESS_WARN)

            addr6 = NULL;

#endif

        }

        n = 0;

        i = ans;

        for (a = 0; a < nan; a++) {

            for ( ;; ) {

                if (buf[i] & 0xc0) {

                    i += 2;

                    break;

                }

                if (buf[i] == 0) {

                    i++;

                    break;

                }

                i += 1 + buf[i];

            }

            an = (ngx_resolver_an_t *) &buf[i];

            type = (an->type_hi << 8) + an->type_lo;

            len = (an->len_hi << 8) + an->len_lo;

            i += sizeof(ngx_resolver_an_t);

            if (type == NGX_RESOLVE_A) {

                addr[n] = htonl((buf[i] << 24) + (buf[i + 1] << 16)

                                + (buf[i + 2] << 8) + (buf[i + 3]));

                if (++n == naddrs) {

#if (NGX_HAVE_INET6)

                    if (rn->naddrs6 == (u_short) -1) {

                        goto next;

                    }

#endif

                    break;

                }

            }

#if (NGX_HAVE_INET6)

            else if (type == NGX_RESOLVE_AAAA) {

                ngx_memcpy(addr6[n].s6_addr, &buf[i], 16);

                if (++n == naddrs) {

                    if (rn->naddrs == (u_short) -1) {

                        goto next;

                    }

                    break;

                }

            }

#endif

            i += len;

        }

    }

    switch (qtype) {

#if (NGX_HAVE_INET6)

    case NGX_RESOLVE_AAAA:

        if (rn->naddrs6 == (u_short) -1) {

            rn->naddrs6 = 0;

        }

        break;

#endif

    default: /* NGX_RESOLVE_A */

        if (rn->naddrs == (u_short) -1) {

            rn->naddrs = 0;

        }

    }

    if (rn->naddrs != (u_short) -1

#if (NGX_HAVE_INET6)

        && rn->naddrs6 != (u_short) -1

#endif

        && rn->naddrs

#if (NGX_HAVE_INET6)

           + rn->naddrs6

#endif

           > 0)

    {

#if (NGX_HAVE_INET6)

    export:

#endif

        naddrs = rn->naddrs;

#if (NGX_HAVE_INET6)

        naddrs += rn->naddrs6;

#endif

        if (naddrs == 1 && rn->naddrs == 1) {

            addrs = NULL;

        } else {

            addrs = ngx_resolver_export(r, rn, 0);

            if (addrs == NULL) {

                goto failed;

            }

        }

        ngx_queue_remove(&rn->queue);

        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);

        rn->expire = ngx_time() + r->expire;

        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);

        next = rn->waiting;

        rn->waiting = NULL;

        /* unlock name mutex */

        while (next) {

            ctx = next;

            ctx->state = NGX_OK;

            ctx->naddrs = naddrs;

            if (addrs == NULL) {

                ctx->addrs = &ctx->addr;

                ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;

                ctx->addr.socklen = sizeof(struct sockaddr_in);

                ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));

                ctx->sin.sin_family = AF_INET;

                ctx->sin.sin_addr.s_addr = rn->u.addr;

            } else {

                ctx->addrs = addrs;

            }

            next = ctx->next;

            ctx->handler(ctx);

        }

        if (addrs != NULL) {

            ngx_resolver_free(r, addrs->sockaddr);

            ngx_resolver_free(r, addrs);

        }

        ngx_resolver_free(r, rn->query);

        rn->query = NULL;

#if (NGX_HAVE_INET6)

        rn->query6 = NULL;

#endif

        return;

    }

    if (cname) {

        /* CNAME only */

        if (rn->naddrs == (u_short) -1

#if (NGX_HAVE_INET6)

            || rn->naddrs6 == (u_short) -1

#endif

            )

        {

            goto next;

        }

        if (ngx_resolver_copy(r, &name, buf, cname, buf + last) != NGX_OK) {

            goto failed;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,

                       "resolver cname:\"%V\"", &name);

        ngx_queue_remove(&rn->queue);

        rn->cnlen = (u_short) name.len;

        rn->u.cname = name.data;

        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);

        rn->expire = ngx_time() + r->expire;

        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);

        ctx = rn->waiting;

        rn->waiting = NULL;

        if (ctx) {

            ctx->name = name;

            (void) ngx_resolve_name_locked(r, ctx);

        }

        ngx_resolver_free(r, rn->query);

        rn->query = NULL;

#if (NGX_HAVE_INET6)

        rn->query6 = NULL;

#endif

        /* unlock name mutex */

        return;

    }

    ngx_log_error(r->log_level, r->log, 0,

                  "no A or CNAME types in DNS response");

    return;

short_response:

    err = "short DNS response";

invalid:

    /* unlock name mutex */

    ngx_log_error(r->log_level, r->log, 0, err);

    return;

failed:

next:

    /* unlock name mutex */

    return;

}

static void

‌ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,

    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)

{

    char                 *err;

    size_t                len;

    u_char                text[NGX_SOCKADDR_STRLEN];

    in_addr_t             addr;

    int32_t               ttl;

    ngx_int_t             octet;

    ngx_str_t             name;

    ngx_uint_t            i, mask, qident, class;

    ngx_queue_t          *expire_queue;

    ngx_rbtree_t         *tree;

    ngx_resolver_an_t    *an;

    ngx_resolver_ctx_t   *ctx, *next;

    ngx_resolver_node_t  *rn;

#if (NGX_HAVE_INET6)

    uint32_t              hash;

    ngx_int_t             digit;

    struct in6_addr       addr6;

#endif

    if (ngx_resolver_copy(r, NULL, buf,

                          buf + sizeof(ngx_resolver_hdr_t), buf + n)

        != NGX_OK)

    {

        return;

    }

    /* AF_INET */

    addr = 0;

    i = sizeof(ngx_resolver_hdr_t);

    for (mask = 0; mask < 32; mask += 8) {

        len = buf[i++];

        octet = ngx_atoi(&buf[i], len);

        if (octet == NGX_ERROR || octet > 255) {

            goto invalid_in_addr_arpa;

        }

        addr += octet << mask;

        i += len;

    }

    if (ngx_strcasecmp(&buf[i], (u_char *) "\7in-addr\4arpa") == 0) {

        i += sizeof("\7in-addr\4arpa");

        /* lock addr mutex */

        rn = ngx_resolver_lookup_addr(r, addr);

        tree = &r->addr_rbtree;

        expire_queue = &r->addr_expire_queue;

        addr = htonl(addr);

        name.len = ngx_inet_ntop(AF_INET, &addr, text, NGX_SOCKADDR_STRLEN);

        name.data = text;

        goto valid;

    }

invalid_in_addr_arpa:

#if (NGX_HAVE_INET6)

    i = sizeof(ngx_resolver_hdr_t);

    for (octet = 15; octet >= 0; octet--) {

        if (buf[i++] != '\1') {

            goto invalid_ip6_arpa;

        }

        digit = ngx_hextoi(&buf[i++], 1);

        if (digit == NGX_ERROR) {

            goto invalid_ip6_arpa;

        }

        addr6.s6_addr[octet] = (u_char) digit;

        if (buf[i++] != '\1') {

            goto invalid_ip6_arpa;

        }

        digit = ngx_hextoi(&buf[i++], 1);

        if (digit == NGX_ERROR) {

            goto invalid_ip6_arpa;

        }

        addr6.s6_addr[octet] += (u_char) (digit * 16);

    }

    if (ngx_strcasecmp(&buf[i], (u_char *) "\3ip6\4arpa") == 0) {

        i += sizeof("\3ip6\4arpa");

        /* lock addr mutex */

        hash = ngx_crc32_short(addr6.s6_addr, 16);

        rn = ngx_resolver_lookup_addr6(r, &addr6, hash);

        tree = &r->addr6_rbtree;

        expire_queue = &r->addr6_expire_queue;

        name.len = ngx_inet6_ntop(addr6.s6_addr, text, NGX_SOCKADDR_STRLEN);

        name.data = text;

        goto valid;

    }

invalid_ip6_arpa:

#endif

    ngx_log_error(r->log_level, r->log, 0,

                  "invalid in-addr.arpa or ip6.arpa name in DNS response");

    return;

valid:

    if (rn == NULL || rn->query == NULL) {

        ngx_log_error(r->log_level, r->log, 0,

                      "unexpected response for %V", &name);

        goto failed;

    }

    qident = (rn->query[0] << 8) + rn->query[1];

    if (ident != qident) {

        ngx_log_error(r->log_level, r->log, 0,

                      "wrong ident %ui response for %V, expect %ui",

                      ident, &name, qident);

        goto failed;

    }

    if (code == 0 && nan == 0) {

        code = NGX_RESOLVE_NXDOMAIN;

    }

    if (code) {

        next = rn->waiting;

        rn->waiting = NULL;

        ngx_queue_remove(&rn->queue);

        ngx_rbtree_delete(tree, &rn->node);

        /* unlock addr mutex */

        while (next) {

            ctx = next;

            ctx->state = code;

            next = ctx->next;

            ctx->handler(ctx);

        }

        ngx_resolver_free_node(r, rn);

        return;

    }

    i += sizeof(ngx_resolver_qs_t);

    if (i + 2 + sizeof(ngx_resolver_an_t) >= n) {

        goto short_response;

    }

    /* compression pointer to *.arpa */

    if (buf[i] != 0xc0 || buf[i + 1] != sizeof(ngx_resolver_hdr_t)) {

        err = "invalid in-addr.arpa or ip6.arpa name in DNS response";

        goto invalid;

    }

    an = (ngx_resolver_an_t *) &buf[i + 2];

    class = (an->class_hi << 8) + an->class_lo;

    len = (an->len_hi << 8) + an->len_lo;

    ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16)

        + (an->ttl[2] << 8) + (an->ttl[3]);

    if (class != 1) {

        ngx_log_error(r->log_level, r->log, 0,

                      "unexpected RR class %ui", class);

        goto failed;

    }

    if (ttl < 0) {

        ttl = 0;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,

                  "resolver qt:%ui cl:%ui len:%uz",

                  (an->type_hi << 8) + an->type_lo,

                  class, len);

    i += 2 + sizeof(ngx_resolver_an_t);

    if (i + len > n) {

        goto short_response;

    }

    if (ngx_resolver_copy(r, &name, buf, buf + i, buf + n) != NGX_OK) {

        goto failed;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name);

    if (name.len != (size_t) rn->nlen

        || ngx_strncmp(name.data, rn->name, name.len) != 0)

    {

        if (rn->nlen) {

            ngx_resolver_free(r, rn->name);

        }

        rn->nlen = (u_short) name.len;

        rn->name = name.data;

        name.data = ngx_resolver_dup(r, rn->name, name.len);

        if (name.data == NULL) {

            goto failed;

        }

    }

    ngx_queue_remove(&rn->queue);

    rn->valid = ngx_time() + (r->valid ? r->valid : ttl);

    rn->expire = ngx_time() + r->expire;

    ngx_queue_insert_head(expire_queue, &rn->queue);

    next = rn->waiting;

    rn->waiting = NULL;

    /* unlock addr mutex */

    while (next) {

        ctx = next;

        ctx->state = NGX_OK;

        ctx->name = name;

        next = ctx->next;

        ctx->handler(ctx);

    }

    ngx_resolver_free(r, name.data);

    return;

short_response:

    err = "short DNS response";

invalid:

    /* unlock addr mutex */

    ngx_log_error(r->log_level, r->log, 0, err);

    return;

failed:

    /* unlock addr mutex */

    return;

}

static ngx_resolver_node_t *

‌ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)

{

    ngx_int_t             rc;

    ngx_rbtree_node_t    *node, *sentinel;

    ngx_resolver_node_t  *rn;

    node = r->name_rbtree.root;

    sentinel = r->name_rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        rn = ngx_resolver_node(node);

        rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);

        if (rc == 0) {

            return rn;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    /* not found */

    return NULL;

}

static ngx_resolver_node_t *

‌ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)

{

    ngx_rbtree_node_t  *node, *sentinel;

    node = r->addr_rbtree.root;

    sentinel = r->addr_rbtree.sentinel;

    while (node != sentinel) {

        if (addr < node->key) {

            node = node->left;

            continue;

        }

        if (addr > node->key) {

            node = node->right;

            continue;

        }

        /* addr == node->key */

        return ngx_resolver_node(node);

    }

    /* not found */

    return NULL;

}

#if (NGX_HAVE_INET6)

static ngx_resolver_node_t *

‌ngx_resolver_lookup_addr6(ngx_resolver_t *r, struct in6_addr *addr,

    uint32_t hash)

{

    ngx_int_t             rc;

    ngx_rbtree_node_t    *node, *sentinel;

    ngx_resolver_node_t  *rn;

    node = r->addr6_rbtree.root;

    sentinel = r->addr6_rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        rn = ngx_resolver_node(node);

        rc = ngx_memcmp(addr, &rn->addr6, 16);

        if (rc == 0) {

            return rn;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    /* not found */

    return NULL;

}

#endif

static void

‌ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t    **p;

    ngx_resolver_node_t   *rn, *rn_temp;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            rn = ngx_resolver_node(node);

            rn_temp = ngx_resolver_node(temp);

            p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)

                 < 0) ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

#if (NGX_HAVE_INET6)

static void

‌ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t    **p;

    ngx_resolver_node_t   *rn, *rn_temp;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            rn = ngx_resolver_node(node);

            rn_temp = ngx_resolver_node(temp);

            p = (ngx_memcmp(&rn->addr6, &rn_temp->addr6, 16)

                 < 0) ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

#endif

static ngx_int_t

‌ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)

{

    u_char              *p, *s;

    size_t               len, nlen;

    ngx_uint_t           ident;

#if (NGX_HAVE_INET6)

    ngx_resolver_t      *r;

#endif

    ngx_resolver_qs_t   *qs;

    ngx_resolver_hdr_t  *query;

    nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1;

    len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);

#if (NGX_HAVE_INET6)

    r = ctx->resolver;

    p = ngx_resolver_alloc(ctx->resolver, r->ipv6 ? len * 2 : len);

#else

    p = ngx_resolver_alloc(ctx->resolver, len);

#endif

    if (p == NULL) {

        return NGX_ERROR;

    }

    rn->qlen = (u_short) len;

    rn->query = p;

#if (NGX_HAVE_INET6)

    if (r->ipv6) {

        rn->query6 = p + len;

    }

#endif

    query = (ngx_resolver_hdr_t *) p;

    ident = ngx_random();

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,

                   "resolve: \"%V\" A %i", &ctx->name, ident & 0xffff);

    query->ident_hi = (u_char) ((ident >> 8) & 0xff);

    query->ident_lo = (u_char) (ident & 0xff);

    /* recursion query */

    query->flags_hi = 1; query->flags_lo = 0;

    /* one question */

    query->nqs_hi = 0; query->nqs_lo = 1;

    query->nan_hi = 0; query->nan_lo = 0;

    query->nns_hi = 0; query->nns_lo = 0;

    query->nar_hi = 0; query->nar_lo = 0;

    p += sizeof(ngx_resolver_hdr_t) + nlen;

    qs = (ngx_resolver_qs_t *) p;

    /* query type */

    qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_A;

    /* IN query class */

    qs->class_hi = 0; qs->class_lo = 1;

    /* convert "www.example.com" to "\3www\7example\3com\0" */

    len = 0;

    p--;

    *p-- = '\0';

    if (ctx->name.len == 0)  {

        return NGX_DECLINED;

    }

    for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) {

        if (*s != '.') {

            *p = *s;

            len++;

        } else {

            if (len == 0 || len > 255) {

                return NGX_DECLINED;

            }

            *p = (u_char) len;

            len = 0;

        }

        p--;

    }

    if (len == 0 || len > 255) {

        return NGX_DECLINED;

    }

    *p = (u_char) len;

#if (NGX_HAVE_INET6)

    if (!r->ipv6) {

        return NGX_OK;

    }

    p = rn->query6;

    ngx_memcpy(p, rn->query, rn->qlen);

    query = (ngx_resolver_hdr_t *) p;

    ident = ngx_random();

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,

                   "resolve: \"%V\" AAAA %i", &ctx->name, ident & 0xffff);

    query->ident_hi = (u_char) ((ident >> 8) & 0xff);

    query->ident_lo = (u_char) (ident & 0xff);

    p += sizeof(ngx_resolver_hdr_t) + nlen;

    qs = (ngx_resolver_qs_t *) p;

    qs->type_lo = NGX_RESOLVE_AAAA;

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)

{

    u_char               *p, *d;

    size_t                len;

    in_addr_t             addr;

    ngx_int_t             n;

    ngx_uint_t            ident;

    ngx_resolver_hdr_t   *query;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        len = sizeof(ngx_resolver_hdr_t)

              + 64 + sizeof(".ip6.arpa.") - 1

              + sizeof(ngx_resolver_qs_t);

        break;

#endif

    default: /* AF_INET */

        len = sizeof(ngx_resolver_hdr_t)

              + sizeof(".255.255.255.255.in-addr.arpa.") - 1

              + sizeof(ngx_resolver_qs_t);

    }

    p = ngx_resolver_alloc(ctx->resolver, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    rn->query = p;

    query = (ngx_resolver_hdr_t *) p;

    ident = ngx_random();

    query->ident_hi = (u_char) ((ident >> 8) & 0xff);

    query->ident_lo = (u_char) (ident & 0xff);

    /* recursion query */

    query->flags_hi = 1; query->flags_lo = 0;

    /* one question */

    query->nqs_hi = 0; query->nqs_lo = 1;

    query->nan_hi = 0; query->nan_lo = 0;

    query->nns_hi = 0; query->nns_lo = 0;

    query->nar_hi = 0; query->nar_lo = 0;

    p += sizeof(ngx_resolver_hdr_t);

    switch (ctx->addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;

        for (n = 15; n >= 0; n--) {

            p = ngx_sprintf(p, "\1%xd\1%xd",

                            sin6->sin6_addr.s6_addr[n] & 0xf,

                            (sin6->sin6_addr.s6_addr[n] >> 4) & 0xf);

        }

        p = ngx_cpymem(p, "\3ip6\4arpa\0", 10);

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) ctx->addr.sockaddr;

        addr = ntohl(sin->sin_addr.s_addr);

        for (n = 0; n < 32; n += 8) {

            d = ngx_sprintf(&p[1], "%ud", (addr >> n) & 0xff);

            *p = (u_char) (d - &p[1]);

            p = d;

        }

        p = ngx_cpymem(p, "\7in-addr\4arpa\0", 14);

    }

    /* query type "PTR", IN query class */

    p = ngx_cpymem(p, "\0\14\0\1", 4);

    rn->qlen = (u_short) (p - rn->query);

    return NGX_OK;

}

static ngx_int_t

‌ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src,

    u_char *last)

{

    char        *err;

    u_char      *p, *dst;

    ssize_t      len;

    ngx_uint_t   i, n;

    p = src;

    len = -1;

    /*

     * compression pointers allow to create endless loop, so we set limit;

     * 128 pointers should be enough to store 255-byte name

     */

    for (i = 0; i < 128; i++) {

        n = *p++;

        if (n == 0) {

            goto done;

        }

        if (n & 0xc0) {

            n = ((n & 0x3f) << 8) + *p;

            p = &buf[n];

        } else {

            len += 1 + n;

            p = &p[n];

        }

        if (p >= last) {

            err = "name is out of response";

            goto invalid;

        }

    }

    err = "compression pointers loop";

invalid:

    ngx_log_error(r->log_level, r->log, 0, err);

    return NGX_ERROR;

done:

    if (name == NULL) {

        return NGX_OK;

    }

    if (len == -1) {

        ngx_str_null(name);

        return NGX_OK;

    }

    dst = ngx_resolver_alloc(r, len);

    if (dst == NULL) {

        return NGX_ERROR;

    }

    name->data = dst;

    n = *src++;

    for ( ;; ) {

        if (n & 0xc0) {

            n = ((n & 0x3f) << 8) + *src;

            src = &buf[n];

            n = *src++;

        } else {

            ngx_strlow(dst, src, n);

            dst += n;

            src += n;

            n = *src++;

            if (n != 0) {

                *dst++ = '.';

            }

        }

        if (n == 0) {

            name->len = dst - name->data;

            return NGX_OK;

        }

    }

}

static void

‌ngx_resolver_timeout_handler(ngx_event_t *ev)

{

    ngx_resolver_ctx_t   *ctx, *next;

    ngx_resolver_node_t  *rn;

    rn = ev->data;

    ctx = rn->waiting;

    rn->waiting = NULL;

    do {

        ctx->state = NGX_RESOLVE_TIMEDOUT;

        next = ctx->next;

        ctx->handler(ctx);

        ctx = next;

    } while (ctx);

}

static void

‌ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn)

{

    /* lock alloc mutex */

    if (rn->query) {

        ngx_resolver_free_locked(r, rn->query);

    }

    if (rn->name) {

        ngx_resolver_free_locked(r, rn->name);

    }

    if (rn->cnlen) {

        ngx_resolver_free_locked(r, rn->u.cname);

    }

    if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) {

        ngx_resolver_free_locked(r, rn->u.addrs);

    }

#if (NGX_HAVE_INET6)

    if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {

        ngx_resolver_free_locked(r, rn->u6.addrs6);

    }

#endif

    ngx_resolver_free_locked(r, rn);

    /* unlock alloc mutex */

}

static void *

‌ngx_resolver_alloc(ngx_resolver_t *r, size_t size)

{

    u_char  *p;

    /* lock alloc mutex */

    p = ngx_alloc(size, r->log);

    /* unlock alloc mutex */

    return p;

}

static void *

‌ngx_resolver_calloc(ngx_resolver_t *r, size_t size)

{

    u_char  *p;

    p = ngx_resolver_alloc(r, size);

    if (p) {

        ngx_memzero(p, size);

    }

    return p;

}

static void

‌ngx_resolver_free(ngx_resolver_t *r, void *p)

{

    /* lock alloc mutex */

    ngx_free(p);

    /* unlock alloc mutex */

}

static void

‌ngx_resolver_free_locked(ngx_resolver_t *r, void *p)

{

    ngx_free(p);

}

static void *

‌ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)

{

    void  *dst;

    dst = ngx_resolver_alloc(r, size);

    if (dst == NULL) {

        return dst;

    }

    ngx_memcpy(dst, src, size);

    return dst;

}

static ngx_addr_t *

‌ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn,

    ngx_uint_t rotate)

{

    ngx_addr_t            *dst;

    ngx_uint_t             d, i, j, n;

    u_char               (*sockaddr)[NGX_SOCKADDRLEN];

    in_addr_t             *addr;

    struct sockaddr_in    *sin;

#if (NGX_HAVE_INET6)

    struct in6_addr       *addr6;

    struct sockaddr_in6   *sin6;

#endif

    n = rn->naddrs;

#if (NGX_HAVE_INET6)

    n += rn->naddrs6;

#endif

    dst = ngx_resolver_calloc(r, n * sizeof(ngx_addr_t));

    if (dst == NULL) {

        return NULL;

    }

    sockaddr = ngx_resolver_calloc(r, n * NGX_SOCKADDRLEN);

    if (sockaddr == NULL) {

        ngx_resolver_free(r, dst);

        return NULL;

    }

    i = 0;

    d = rotate ? ngx_random() % n : 0;

    if (rn->naddrs) {

        j = rotate ? ngx_random() % rn->naddrs : 0;

        addr = (rn->naddrs == 1) ? &rn->u.addr : rn->u.addrs;

        do {

            sin = (struct sockaddr_in *) sockaddr[d];

            sin->sin_family = AF_INET;

            sin->sin_addr.s_addr = addr[j++];

            dst[d].sockaddr = (struct sockaddr *) sin;

            dst[d++].socklen = sizeof(struct sockaddr_in);

            if (d == n) {

                d = 0;

            }

            if (j == rn->naddrs) {

                j = 0;

            }

        } while (++i < rn->naddrs);

    }

#if (NGX_HAVE_INET6)

    if (rn->naddrs6) {

        j = rotate ? ngx_random() % rn->naddrs6 : 0;

        addr6 = (rn->naddrs6 == 1) ? &rn->u6.addr6 : rn->u6.addrs6;

        do {

            sin6 = (struct sockaddr_in6 *) sockaddr[d];

            sin6->sin6_family = AF_INET6;

            ngx_memcpy(sin6->sin6_addr.s6_addr, addr6[j++].s6_addr, 16);

            dst[d].sockaddr = (struct sockaddr *) sin6;

            dst[d++].socklen = sizeof(struct sockaddr_in6);

            if (d == n) {

                d = 0;

            }

            if (j == rn->naddrs6) {

                j = 0;

            }

        } while (++i < n);

    }

#endif

    return dst;

}

char *

‌ngx_resolver_strerror(ngx_int_t err)

{

    static char *errors[] = {

        "Format error",     /* FORMERR */

        "Server failure",   /* SERVFAIL */

        "Host not found",   /* NXDOMAIN */

        "Unimplemented",    /* NOTIMP */

        "Operation refused" /* REFUSED */

    };

    if (err > 0 && err < 6) {

        return errors[err - 1];

    }

    if (err == NGX_RESOLVE_TIMEDOUT) {

        return "Operation timed out";

    }

    return "Unknown error";

}

static u_char *

‌ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len)

{

    u_char                *p;

    ngx_udp_connection_t  *uc;

    p = buf;

    if (log->action) {

        p = ngx_snprintf(buf, len, " while %s", log->action);

        len -= p - buf;

    }

    uc = log->data;

    if (uc) {

        p = ngx_snprintf(p, len, ", resolver: %V", &uc->server);

    }

    return p;

}

ngx_int_t

‌ngx_udp_connect(ngx_udp_connection_t *uc)

{

    int                rc;

    ngx_int_t          event;

    ngx_event_t       *rev, *wev;

    ngx_socket_t       s;

    ngx_connection_t  *c;

    s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s);

    if (s == (ngx_socket_t) -1) {

        ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,

                      ngx_socket_n " failed");

        return NGX_ERROR;

    }

    c = ngx_get_connection(s, &uc->log);

    if (c == NULL) {

        if (ngx_close_socket(s) == -1) {

            ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,

                          ngx_close_socket_n "failed");

        }

        return NGX_ERROR;

    }

    if (ngx_nonblocking(s) == -1) {

        ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,

                      ngx_nonblocking_n " failed");

        goto failed;

    }

    rev = c->read;

    wev = c->write;

    rev->log = &uc->log;

    wev->log = &uc->log;

    uc->connection = c;

    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0,

                   "connect to %V, fd:%d #%uA", &uc->server, s, c->number);

    rc = connect(s, uc->sockaddr, uc->socklen);

    /* TODO: aio, iocp */

    if (rc == -1) {

        ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,

                      "connect() failed");

        goto failed;

    }

    /* UDP sockets are always ready to write */

    wev->ready = 1;

    if (ngx_add_event) {

        event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?

                    /* kqueue, epoll */                 NGX_CLEAR_EVENT:

                    /* select, poll, /dev/poll */       NGX_LEVEL_EVENT;

                    /* eventport event type has no meaning: oneshot only */

        if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {

            goto failed;

        }

    } else {

        /* rtsig */

        if (ngx_add_conn(c) == NGX_ERROR) {

            goto failed;

        }

    }

    return NGX_OK;

failed:

    ngx_close_connection(c);

    uc->connection = NULL;

    return NGX_ERROR;

}

src/core/ngx_parse.c - nginx-1.7.9

Functions defined

ngx_parse_offset

ngx_parse_size

ngx_parse_time

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

ssize_t

‌ngx_parse_size(ngx_str_t *line)

{

    u_char     unit;

    size_t     len;

    ssize_t    size;

    ngx_int_t  scale;

    len = line->len;

    unit = line->data[len - 1];

    switch (unit) {

    case 'K':

    case 'k':

        len--;

        scale = 1024;

        break;

    case 'M':

    case 'm':

        len--;

        scale = 1024 * 1024;

        break;

    default:

        scale = 1;

    }

    size = ngx_atosz(line->data, len);

    if (size == NGX_ERROR) {

        return NGX_ERROR;

    }

    size *= scale;

    return size;

}

off_t

‌ngx_parse_offset(ngx_str_t *line)

{

    u_char     unit;

    off_t      offset;

    size_t     len;

    ngx_int_t  scale;

    len = line->len;

    unit = line->data[len - 1];

    switch (unit) {

    case 'K':

    case 'k':

        len--;

        scale = 1024;

        break;

    case 'M':

    case 'm':

        len--;

        scale = 1024 * 1024;

        break;

    case 'G':

    case 'g':

        len--;

        scale = 1024 * 1024 * 1024;

        break;

    default:

        scale = 1;

    }

    offset = ngx_atoof(line->data, len);

    if (offset == NGX_ERROR) {

        return NGX_ERROR;

    }

    offset *= scale;

    return offset;

}

ngx_int_t

‌ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec)

{

    u_char      *p, *last;

    ngx_int_t    value, total, scale;

    ngx_uint_t   max, valid;

    enum {

        st_start = 0,

        st_year,

        st_month,

        st_week,

        st_day,

        st_hour,

        st_min,

        st_sec,

        st_msec,

        st_last

    } step;

    valid = 0;

    value = 0;

    total = 0;

    step = is_sec ? st_start : st_month;

    scale = is_sec ? 1 : 1000;

    p = line->data;

    last = p + line->len;

    while (p < last) {

        if (*p >= '0' && *p <= '9') {

            value = value * 10 + (*p++ - '0');

            valid = 1;

            continue;

        }

        switch (*p++) {

        case 'y':

            if (step > st_start) {

                return NGX_ERROR;

            }

            step = st_year;

            max = NGX_MAX_INT32_VALUE / (60 * 60 * 24 * 365);

            scale = 60 * 60 * 24 * 365;

            break;

        case 'M':

            if (step >= st_month) {

                return NGX_ERROR;

            }

            step = st_month;

            max = NGX_MAX_INT32_VALUE / (60 * 60 * 24 * 30);

            scale = 60 * 60 * 24 * 30;

            break;

        case 'w':

            if (step >= st_week) {

                return NGX_ERROR;

            }

            step = st_week;

            max = NGX_MAX_INT32_VALUE / (60 * 60 * 24 * 7);

            scale = 60 * 60 * 24 * 7;

            break;

        case 'd':

            if (step >= st_day) {

                return NGX_ERROR;

            }

            step = st_day;

            max = NGX_MAX_INT32_VALUE / (60 * 60 * 24);

            scale = 60 * 60 * 24;

            break;

        case 'h':

            if (step >= st_hour) {

                return NGX_ERROR;

            }

            step = st_hour;

            max = NGX_MAX_INT32_VALUE / (60 * 60);

            scale = 60 * 60;

            break;

        case 'm':

            if (*p == 's') {

                if (is_sec || step >= st_msec) {

                    return NGX_ERROR;

                }

                p++;

                step = st_msec;

                max = NGX_MAX_INT32_VALUE;

                scale = 1;

                break;

            }

            if (step >= st_min) {

                return NGX_ERROR;

            }

            step = st_min;

            max = NGX_MAX_INT32_VALUE / 60;

            scale = 60;

            break;

        case 's':

            if (step >= st_sec) {

                return NGX_ERROR;

            }

            step = st_sec;

            max = NGX_MAX_INT32_VALUE;

            scale = 1;

            break;

        case ' ':

            if (step >= st_sec) {

                return NGX_ERROR;

            }

            step = st_last;

            max = NGX_MAX_INT32_VALUE;

            scale = 1;

            break;

        default:

            return NGX_ERROR;

        }

        if (step != st_msec && !is_sec) {

            scale *= 1000;

            max /= 1000;

        }

        if ((ngx_uint_t) value > max) {

            return NGX_ERROR;

        }

        total += value * scale;

        if ((ngx_uint_t) total > NGX_MAX_INT32_VALUE) {

            return NGX_ERROR;

        }

        value = 0;

        scale = is_sec ? 1 : 1000;

        while (p < last && *p == ' ') {

            p++;

        }

    }

    if (valid) {

        return total + value * scale;

    }

    return NGX_ERROR;

}

src/event/ngx_event_connect.h - nginx-1.7.9

Data types defined

ngx_event_free_peer_pt

ngx_event_get_peer_pt

ngx_event_save_peer_session_pt

ngx_event_set_peer_session_pt

ngx_peer_connection_s

ngx_peer_connection_t

Macros defined

NGX_PEER_FAILED

NGX_PEER_KEEPALIVE

NGX_PEER_NEXT

_NGX_EVENT_CONNECT_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_

‌#define _NGX_EVENT_CONNECT_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define NGX_PEER_KEEPALIVE           1

‌#define NGX_PEER_NEXT                2

‌#define NGX_PEER_FAILED              4

‌typedef struct ngx_peer_connection_s  ngx_peer_connection_t;

‌typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,

    void *data);

‌typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,

    ngx_uint_t state);

#if (NGX_SSL)

‌typedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc,

    void *data);

‌typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc,

    void *data);

#endif

‌struct ngx_peer_connection_s {

    ngx_connection_t                *connection;

    struct sockaddr                 *sockaddr;

    socklen_t                        socklen;

    ngx_str_t                       *name;

    ngx_uint_t                       tries;

    ngx_msec_t                       start_time;

    ngx_event_get_peer_pt            get;

    ngx_event_free_peer_pt           free;

    void                            *data;

#if (NGX_SSL)

    ngx_event_set_peer_session_pt    set_session;

    ngx_event_save_peer_session_pt   save_session;

#endif

#if (NGX_THREADS)

    ngx_atomic_t                    *lock;

#endif

    ngx_addr_t                      *local;

    int                              rcvbuf;

    ngx_log_t                       *log;

    unsigned                         cached:1;

                                     /* ngx_connection_log_error_e */

    unsigned                         log_error:2;

};

ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);

ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);

#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */

src/event/ngx_event_connect.c - nginx-1.7.9

Functions defined

ngx_event_connect_peer

ngx_event_get_peer

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

ngx_int_t

‌ngx_event_connect_peer(ngx_peer_connection_t *pc)

{

    int                rc;

    ngx_int_t          event;

    ngx_err_t          err;

    ngx_uint_t         level;

    ngx_socket_t       s;

    ngx_event_t       *rev, *wev;

    ngx_connection_t  *c;

    rc = pc->get(pc, pc->data);

    if (rc != NGX_OK) {

        return rc;

    }

    s = ngx_socket(pc->sockaddr->sa_family, SOCK_STREAM, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0, "socket %d", s);

    if (s == (ngx_socket_t) -1) {

        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,

                      ngx_socket_n " failed");

        return NGX_ERROR;

    }

    c = ngx_get_connection(s, pc->log);

    if (c == NULL) {

        if (ngx_close_socket(s) == -1) {

            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,

                          ngx_close_socket_n "failed");

        }

        return NGX_ERROR;

    }

    if (pc->rcvbuf) {

        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,

                       (const void *) &pc->rcvbuf, sizeof(int)) == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,

                          "setsockopt(SO_RCVBUF) failed");

            goto failed;

        }

    }

    if (ngx_nonblocking(s) == -1) {

        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,

                      ngx_nonblocking_n " failed");

        goto failed;

    }

    if (pc->local) {

        if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {

            ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,

                          "bind(%V) failed", &pc->local->name);

            goto failed;

        }

    }

    c->recv = ngx_recv;

    c->send = ngx_send;

    c->recv_chain = ngx_recv_chain;

    c->send_chain = ngx_send_chain;

    c->sendfile = 1;

    c->log_error = pc->log_error;

    if (pc->sockaddr->sa_family == AF_UNIX) {

        c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;

        c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;

#if (NGX_SOLARIS)

        /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */

        c->sendfile = 0;

#endif

    }

    rev = c->read;

    wev = c->write;

    rev->log = pc->log;

    wev->log = pc->log;

    pc->connection = c;

    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);

    if (ngx_add_conn) {

        if (ngx_add_conn(c) == NGX_ERROR) {

            goto failed;

        }

    }

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,

                   "connect to %V, fd:%d #%uA", pc->name, s, c->number);

    rc = connect(s, pc->sockaddr, pc->socklen);

    if (rc == -1) {

        err = ngx_socket_errno;

        if (err != NGX_EINPROGRESS

#if (NGX_WIN32)

            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */

            && err != NGX_EAGAIN

#endif

            )

        {

            if (err == NGX_ECONNREFUSED

#if (NGX_LINUX)

                /*

                 * Linux returns EAGAIN instead of ECONNREFUSED

                 * for unix sockets if listen queue is full

                 */

                || err == NGX_EAGAIN

#endif

                || err == NGX_ECONNRESET

                || err == NGX_ENETDOWN

                || err == NGX_ENETUNREACH

                || err == NGX_EHOSTDOWN

                || err == NGX_EHOSTUNREACH)

            {

                level = NGX_LOG_ERR;

            } else {

                level = NGX_LOG_CRIT;

            }

            ngx_log_error(level, c->log, err, "connect() to %V failed",

                          pc->name);

            ngx_close_connection(c);

            pc->connection = NULL;

            return NGX_DECLINED;

        }

    }

    if (ngx_add_conn) {

        if (rc == -1) {

            /* NGX_EINPROGRESS */

            return NGX_AGAIN;

        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");

        wev->ready = 1;

        return NGX_OK;

    }

    if (ngx_event_flags & NGX_USE_AIO_EVENT) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,

                       "connect(): %d", rc);

        /* aio, iocp */

        if (ngx_blocking(s) == -1) {

            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,

                          ngx_blocking_n " failed");

            goto failed;

        }

        /*

         * FreeBSD's aio allows to post an operation on non-connected socket.

         * NT does not support it.

         *

         * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT

         */

        rev->ready = 1;

        wev->ready = 1;

        return NGX_OK;

    }

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue */

        event = NGX_CLEAR_EVENT;

    } else {

        /* select, poll, /dev/poll */

        event = NGX_LEVEL_EVENT;

    }

    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {

        goto failed;

    }

    if (rc == -1) {

        /* NGX_EINPROGRESS */

        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {

            goto failed;

        }

        return NGX_AGAIN;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");

    wev->ready = 1;

    return NGX_OK;

failed:

    ngx_close_connection(c);

    pc->connection = NULL;

    return NGX_ERROR;

}

ngx_int_t

‌ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)

{

    return NGX_OK;

}

src/os/unix/ngx_solaris_sendfilev_chain.c - nginx-1.7.9

Data types defined

sendfilevec

sendfilevec_t

Functions defined

ngx_solaris_sendfilev_chain

sendfilev

Macros defined

NGX_SENDFILEVECS

SFV_FD_SELF

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_TEST_BUILD_SOLARIS_SENDFILEV)

/* Solaris declarations */

‌typedef struct sendfilevec {

    int     sfv_fd;

    u_int   sfv_flag;

    off_t   sfv_off;

    size_t  sfv_len;

‌} sendfilevec_t;

‌#define SFV_FD_SELF  -2

‌static ssize_t sendfilev(int fd, const struct sendfilevec *vec,

    int sfvcnt, size_t *xferred)

{

    return -1;

}

ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

#endif

‌#define NGX_SENDFILEVECS  NGX_IOVS_PREALLOCATE

ngx_chain_t *

‌ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    int             fd;

    u_char         *prev;

    off_t           size, send, prev_send, aligned, fprev;

    size_t          sent;

    ssize_t         n;

    ngx_int_t       eintr;

    ngx_err_t       err;

    ngx_uint_t      nsfv;

    sendfilevec_t  *sfv, sfvs[NGX_SENDFILEVECS];

    ngx_event_t    *wev;

    ngx_chain_t    *cl;

    wev = c->write;

    if (!wev->ready) {

        return in;

    }

    if (!c->sendfile) {

        return ngx_writev_chain(c, in, limit);

    }

    /* the maximum limit size is the maximum size_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;

    }

    send = 0;

    for ( ;; ) {

        fd = SFV_FD_SELF;

        prev = NULL;

        fprev = 0;

        sfv = NULL;

        eintr = 0;

        sent = 0;

        prev_send = send;

        nsfv = 0;

        /* create the sendfilevec and coalesce the neighbouring bufs */

        for (cl = in; cl && send < limit; cl = cl->next) {

            if (ngx_buf_special(cl->buf)) {

                continue;

            }

            if (ngx_buf_in_memory_only(cl->buf)) {

                fd = SFV_FD_SELF;

                size = cl->buf->last - cl->buf->pos;

                if (send + size > limit) {

                    size = limit - send;

                }

                if (prev == cl->buf->pos) {

                    sfv->sfv_len += (size_t) size;

                } else {

                    if (nsfv == NGX_SENDFILEVECS) {

                        break;

                    }

                    sfv = &sfvs[nsfv++];

                    sfv->sfv_fd = SFV_FD_SELF;

                    sfv->sfv_flag = 0;

                    sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos;

                    sfv->sfv_len = (size_t) size;

                }

                prev = cl->buf->pos + (size_t) size;

                send += size;

            } else {

                prev = NULL;

                size = cl->buf->file_last - cl->buf->file_pos;

                if (send + size > limit) {

                    size = limit - send;

                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)

                               & ~((off_t) ngx_pagesize - 1);

                    if (aligned <= cl->buf->file_last) {

                        size = aligned - cl->buf->file_pos;

                    }

                }

                if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) {

                    sfv->sfv_len += (size_t) size;

                } else {

                    if (nsfv == NGX_SENDFILEVECS) {

                        break;

                    }

                    sfv = &sfvs[nsfv++];

                    fd = cl->buf->file->fd;

                    sfv->sfv_fd = fd;

                    sfv->sfv_flag = 0;

                    sfv->sfv_off = cl->buf->file_pos;

                    sfv->sfv_len = (size_t) size;

                }

                fprev = cl->buf->file_pos + size;

                send += size;

            }

        }

        n = sendfilev(c->fd, sfvs, nsfv, &sent);

        if (n == -1) {

            err = ngx_errno;

            switch (err) {

            case NGX_EAGAIN:

                break;

            case NGX_EINTR:

                eintr = 1;

                break;

            default:

                wev->error = 1;

                ngx_connection_error(c, err, "sendfilev() failed");

                return NGX_CHAIN_ERROR;

            }

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,

                          "sendfilev() sent only %uz bytes", sent);

        }

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "sendfilev: %z %z", n, sent);

        c->sent += sent;

        in = ngx_chain_update_sent(in, sent);

        if (eintr) {

            send = prev_send + sent;

            continue;

        }

        if (send - prev_send != (off_t) sent) {

            wev->ready = 0;

            return in;

        }

        if (send >= limit || in == NULL) {

            return in;

        }

    }

}

src/os/unix/ngx_writev_chain.c - nginx-1.7.9

Functions defined

ngx_output_chain_to_iovec

ngx_writev

ngx_writev_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

ngx_chain_t *

‌ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    ssize_t        n, sent;

    off_t          send, prev_send;

    ngx_chain_t   *cl;

    ngx_event_t   *wev;

    ngx_iovec_t    vec;

    struct iovec   iovs[NGX_IOVS_PREALLOCATE];

    wev = c->write;

    if (!wev->ready) {

        return in;

    }

#if (NGX_HAVE_KQUEUE)

    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {

        (void) ngx_connection_error(c, wev->kq_errno,

                               "kevent() reported about an closed connection");

        wev->error = 1;

        return NGX_CHAIN_ERROR;

    }

#endif

    /* the maximum limit size is the maximum size_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;

    }

    send = 0;

    vec.iovs = iovs;

    vec.nalloc = NGX_IOVS_PREALLOCATE;

    for ( ;; ) {

        prev_send = send;

        /* create the iovec and coalesce the neighbouring bufs */

        cl = ngx_output_chain_to_iovec(&vec, in, limit - send, c->log);

        if (cl == NGX_CHAIN_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        if (cl && cl->buf->in_file) {

            ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                          "file buf in writev "

                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",

                          cl->buf->temporary,

                          cl->buf->recycled,

                          cl->buf->in_file,

                          cl->buf->start,

                          cl->buf->pos,

                          cl->buf->last,

                          cl->buf->file,

                          cl->buf->file_pos,

                          cl->buf->file_last);

            ngx_debug_point();

            return NGX_CHAIN_ERROR;

        }

        send += vec.size;

        n = ngx_writev(c, &vec);

        if (n == NGX_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        sent = (n == NGX_AGAIN) ? 0 : n;

        c->sent += sent;

        in = ngx_chain_update_sent(in, sent);

        if (send - prev_send != sent) {

            wev->ready = 0;

            return in;

        }

        if (send >= limit || in == NULL) {

            return in;

        }

    }

}

ngx_chain_t *

‌ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, size_t limit,

    ngx_log_t *log)

{

    size_t         total, size;

    u_char        *prev;

    ngx_uint_t     n;

    struct iovec  *iov;

    iov = NULL;

    prev = NULL;

    total = 0;

    n = 0;

    for ( /* void */ ; in && total < limit; in = in->next) {

        if (ngx_buf_special(in->buf)) {

            continue;

        }

        if (in->buf->in_file) {

            break;

        }

        if (!ngx_buf_in_memory(in->buf)) {

            ngx_log_error(NGX_LOG_ALERT, log, 0,

                          "bad buf in output chain "

                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",

                          in->buf->temporary,

                          in->buf->recycled,

                          in->buf->in_file,

                          in->buf->start,

                          in->buf->pos,

                          in->buf->last,

                          in->buf->file,

                          in->buf->file_pos,

                          in->buf->file_last);

            ngx_debug_point();

            return NGX_CHAIN_ERROR;

        }

        size = in->buf->last - in->buf->pos;

        if (size > limit - total) {

            size = limit - total;

        }

        if (prev == in->buf->pos) {

            iov->iov_len += size;

        } else {

            if (n == vec->nalloc) {

                break;

            }

            iov = &vec->iovs[n++];

            iov->iov_base = (void *) in->buf->pos;

            iov->iov_len = size;

        }

        prev = in->buf->pos + size;

        total += size;

    }

    vec->count = n;

    vec->size = total;

    return in;

}

ssize_t

‌ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec)

{

    ssize_t    n;

    ngx_err_t  err;

eintr:

    n = writev(c->fd, vec->iovs, vec->count);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "writev: %z of %uz", n, vec->size);

    if (n == -1) {

        err = ngx_errno;

        switch (err) {

        case NGX_EAGAIN:

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "writev() not ready");

            return NGX_AGAIN;

        case NGX_EINTR:

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "writev() was interrupted");

            goto eintr;

        default:

            c->write->error = 1;

            ngx_connection_error(c, err, "writev() failed");

            return NGX_ERROR;

        }

    }

    return n;

}

src/http/modules/ngx_http_ssl_module.c - nginx-1.7.9

Global variables defined

ngx_http_ssl_commands

ngx_http_ssl_module

ngx_http_ssl_module_ctx

ngx_http_ssl_protocols

ngx_http_ssl_sess_id_ctx

ngx_http_ssl_vars

ngx_http_ssl_verify

Data types defined

ngx_ssl_variable_handler_pt

Functions defined

ngx_http_ssl_add_variables

ngx_http_ssl_alpn_select

ngx_http_ssl_create_srv_conf

ngx_http_ssl_enable

ngx_http_ssl_init

ngx_http_ssl_merge_srv_conf

ngx_http_ssl_npn_advertised

ngx_http_ssl_password_file

ngx_http_ssl_session_cache

ngx_http_ssl_static_variable

ngx_http_ssl_variable

Macros defined

NGX_DEFAULT_CIPHERS

NGX_DEFAULT_ECDH_CURVE

NGX_HTTP_NPN_ADVERTISE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,

    ngx_pool_t *pool, ngx_str_t *s);

‌#define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"

‌#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"

‌#define NGX_HTTP_NPN_ADVERTISE  "\x08http/1.1"

#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation

static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,

    const unsigned char **out, unsigned char *outlen,

    const unsigned char *in, unsigned int inlen, void *arg);

#endif

#ifdef TLSEXT_TYPE_next_proto_neg

static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,

    const unsigned char **out, unsigned int *outlen, void *arg);

#endif

static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);

static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);

static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,

    void *parent, void *child);

static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);

‌static ngx_conf_bitmask_t  ngx_http_ssl_protocols[] = {

    { ngx_string("SSLv2"), NGX_SSL_SSLv2 },

    { ngx_string("SSLv3"), NGX_SSL_SSLv3 },

    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },

    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },

    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },

    { ngx_null_string, 0 }

};

‌static ngx_conf_enum_t  ngx_http_ssl_verify[] = {

    { ngx_string("off"), 0 },

    { ngx_string("on"), 1 },

    { ngx_string("optional"), 2 },

    { ngx_string("optional_no_ca"), 3 },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_http_ssl_commands[] = {

    { ngx_string("ssl"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_http_ssl_enable,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, enable),

      NULL },

    { ngx_string("ssl_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, certificate),

      NULL },

    { ngx_string("ssl_certificate_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, certificate_key),

      NULL },

    { ngx_string("ssl_password_file"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_http_ssl_password_file,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ssl_dhparam"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, dhparam),

      NULL },

    { ngx_string("ssl_ecdh_curve"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, ecdh_curve),

      NULL },

    { ngx_string("ssl_protocols"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, protocols),

      &ngx_http_ssl_protocols },

    { ngx_string("ssl_ciphers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, ciphers),

      NULL },

    { ngx_string("ssl_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, buffer_size),

      NULL },

    { ngx_string("ssl_verify_client"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, verify),

      &ngx_http_ssl_verify },

    { ngx_string("ssl_verify_depth"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, verify_depth),

      NULL },

    { ngx_string("ssl_client_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, client_certificate),

      NULL },

    { ngx_string("ssl_trusted_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),

      NULL },

    { ngx_string("ssl_prefer_server_ciphers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),

      NULL },

    { ngx_string("ssl_session_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,

      ngx_http_ssl_session_cache,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ssl_session_tickets"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, session_tickets),

      NULL },

    { ngx_string("ssl_session_ticket_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, session_ticket_keys),

      NULL },

    { ngx_string("ssl_session_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_sec_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, session_timeout),

      NULL },

    { ngx_string("ssl_crl"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, crl),

      NULL },

    { ngx_string("ssl_stapling"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, stapling),

      NULL },

    { ngx_string("ssl_stapling_file"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, stapling_file),

      NULL },

    { ngx_string("ssl_stapling_responder"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),

      NULL },

    { ngx_string("ssl_stapling_verify"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_ssl_module_ctx = {

    ngx_http_ssl_add_variables,            /* preconfiguration */

    ngx_http_ssl_init,                     /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_http_ssl_create_srv_conf,          /* create server configuration */

    ngx_http_ssl_merge_srv_conf,           /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_ssl_module = {

    NGX_MODULE_V1,

    &ngx_http_ssl_module_ctx,              /* module context */

    ngx_http_ssl_commands,                 /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_variable_t  ngx_http_ssl_vars[] = {

    { ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,

      (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,

      (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_session_reused"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_server_name"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_server_name, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_raw_certificate,

      NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_fingerprint"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_fingerprint, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,

      (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");

#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation

static int

‌ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,

    unsigned char *outlen, const unsigned char *in, unsigned int inlen,

    void *arg)

{

    unsigned int            srvlen;

    unsigned char          *srv;

#if (NGX_DEBUG)

    unsigned int            i;

#endif

#if (NGX_HTTP_SPDY)

    ngx_http_connection_t  *hc;

#endif

#if (NGX_HTTP_SPDY || NGX_DEBUG)

    ngx_connection_t       *c;

    c = ngx_ssl_get_connection(ssl_conn);

#endif

#if (NGX_DEBUG)

    for (i = 0; i < inlen; i += in[i] + 1) {

         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                        "SSL ALPN supported by client: %*s", in[i], &in[i + 1]);

    }

#endif

#if (NGX_HTTP_SPDY)

    hc = c->data;

    if (hc->addr_conf->spdy) {

        srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;

        srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;

    } else

#endif

    {

        srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;

        srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;

    }

    if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,

                              in, inlen)

        != OPENSSL_NPN_NEGOTIATED)

    {

        return SSL_TLSEXT_ERR_NOACK;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "SSL ALPN selected: %*s", *outlen, *out);

    return SSL_TLSEXT_ERR_OK;

}

#endif

#ifdef TLSEXT_TYPE_next_proto_neg

static int

‌ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,

    const unsigned char **out, unsigned int *outlen, void *arg)

{

#if (NGX_HTTP_SPDY || NGX_DEBUG)

    ngx_connection_t  *c;

    c = ngx_ssl_get_connection(ssl_conn);

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised");

#endif

#if (NGX_HTTP_SPDY)

    {

    ngx_http_connection_t  *hc;

    hc = c->data;

    if (hc->addr_conf->spdy) {

        *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;

        *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;

        return SSL_TLSEXT_ERR_OK;

    }

    }

#endif

    *out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;

    *outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;

    return SSL_TLSEXT_ERR_OK;

}

#endif

static ngx_int_t

‌ngx_http_ssl_static_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;

    size_t     len;

    ngx_str_t  s;

    if (r->connection->ssl) {

        (void) handler(r->connection, NULL, &s);

        v->data = s.data;

        for (len = 0; v->data[len]; len++) { /* void */ }

        v->len = len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        return NGX_OK;

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;

    ngx_str_t  s;

    if (r->connection->ssl) {

        if (handler(r->connection, r->pool, &s) != NGX_OK) {

            return NGX_ERROR;

        }

        v->len = s.len;

        v->data = s.data;

        if (v->len) {

            v->valid = 1;

            v->no_cacheable = 0;

            v->not_found = 0;

            return NGX_OK;

        }

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssl_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_ssl_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static void *

‌ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)

{

    ngx_http_ssl_srv_conf_t  *sscf;

    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));

    if (sscf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     sscf->protocols = 0;

     *     sscf->certificate = { 0, NULL };

     *     sscf->certificate_key = { 0, NULL };

     *     sscf->dhparam = { 0, NULL };

     *     sscf->ecdh_curve = { 0, NULL };

     *     sscf->client_certificate = { 0, NULL };

     *     sscf->trusted_certificate = { 0, NULL };

     *     sscf->crl = { 0, NULL };

     *     sscf->ciphers = { 0, NULL };

     *     sscf->shm_zone = NULL;

     *     sscf->stapling_file = { 0, NULL };

     *     sscf->stapling_responder = { 0, NULL };

     */

    sscf->enable = NGX_CONF_UNSET;

    sscf->prefer_server_ciphers = NGX_CONF_UNSET;

    sscf->buffer_size = NGX_CONF_UNSET_SIZE;

    sscf->verify = NGX_CONF_UNSET_UINT;

    sscf->verify_depth = NGX_CONF_UNSET_UINT;

    sscf->passwords = NGX_CONF_UNSET_PTR;

    sscf->builtin_session_cache = NGX_CONF_UNSET;

    sscf->session_timeout = NGX_CONF_UNSET;

    sscf->session_tickets = NGX_CONF_UNSET;

    sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;

    sscf->stapling = NGX_CONF_UNSET;

    sscf->stapling_verify = NGX_CONF_UNSET;

    return sscf;

}

static char *

‌ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_ssl_srv_conf_t *prev = parent;

    ngx_http_ssl_srv_conf_t *conf = child;

    ngx_pool_cleanup_t  *cln;

    if (conf->enable == NGX_CONF_UNSET) {

        if (prev->enable == NGX_CONF_UNSET) {

            conf->enable = 0;

        } else {

            conf->enable = prev->enable;

            conf->file = prev->file;

            conf->line = prev->line;

        }

    }

    ngx_conf_merge_value(conf->session_timeout,

                         prev->session_timeout, 300);

    ngx_conf_merge_value(conf->prefer_server_ciphers,

                         prev->prefer_server_ciphers, 0);

    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,

                         (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1

                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));

    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,

                         NGX_SSL_BUFSIZE);

    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);

    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);

    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");

    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");

    ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);

    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");

    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,

                         "");

    ngx_conf_merge_str_value(conf->trusted_certificate,

                         prev->trusted_certificate, "");

    ngx_conf_merge_str_value(conf->crl, prev->crl, "");

    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,

                         NGX_DEFAULT_ECDH_CURVE);

    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);

    ngx_conf_merge_value(conf->stapling, prev->stapling, 0);

    ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);

    ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");

    ngx_conf_merge_str_value(conf->stapling_responder,

                         prev->stapling_responder, "");

    conf->ssl.log = cf->log;

    if (conf->enable) {

        if (conf->certificate.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate\" is defined for "

                          "the \"ssl\" directive in %s:%ui",

                          conf->file, conf->line);

            return NGX_CONF_ERROR;

        }

        if (conf->certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate_key\" is defined for "

                          "the \"ssl\" directive in %s:%ui",

                          conf->file, conf->line);

            return NGX_CONF_ERROR;

        }

    } else {

        if (conf->certificate.len == 0) {

            return NGX_CONF_OK;

        }

        if (conf->certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate_key\" is defined "

                          "for certificate \"%V\"", &conf->certificate);

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

    if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,

                                               ngx_http_ssl_servername)

        == 0)

    {

        ngx_log_error(NGX_LOG_WARN, cf->log, 0,

            "nginx was built with SNI support, however, now it is linked "

            "dynamically to an OpenSSL library which has no tlsext support, "

            "therefore SNI is not available");

    }

#endif

#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation

    SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_http_ssl_alpn_select, NULL);

#endif

#ifdef TLSEXT_TYPE_next_proto_neg

    SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx,

                                          ngx_http_ssl_npn_advertised, NULL);

#endif

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_CONF_ERROR;

    }

    cln->handler = ngx_ssl_cleanup_ctx;

    cln->data = &conf->ssl;

    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,

                            &conf->certificate_key, conf->passwords)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    if (SSL_CTX_set_cipher_list(conf->ssl.ctx,

                                (const char *) conf->ciphers.data)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,

                      "SSL_CTX_set_cipher_list(\"%V\") failed",

                      &conf->ciphers);

        return NGX_CONF_ERROR;

    }

    conf->ssl.buffer_size = conf->buffer_size;

    if (conf->verify) {

        if (conf->client_certificate.len == 0 && conf->verify != 3) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no ssl_client_certificate for ssl_client_verify");

            return NGX_CONF_ERROR;

        }

        if (ngx_ssl_client_certificate(cf, &conf->ssl,

                                       &conf->client_certificate,

                                       conf->verify_depth)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_ssl_trusted_certificate(cf, &conf->ssl,

                                    &conf->trusted_certificate,

                                    conf->verify_depth)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (conf->prefer_server_ciphers) {

        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);

    }

    /* a temporary 512-bit RSA key is required for export versions of MSIE */

    SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);

    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->builtin_session_cache,

                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);

    if (conf->shm_zone == NULL) {

        conf->shm_zone = prev->shm_zone;

    }

    if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,

                              conf->builtin_session_cache,

                              conf->shm_zone, conf->session_timeout)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1);

#ifdef SSL_OP_NO_TICKET

    if (!conf->session_tickets) {

        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);

    }

#endif

    ngx_conf_merge_ptr_value(conf->session_ticket_keys,

                         prev->session_ticket_keys, NULL);

    if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    if (conf->stapling) {

        if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,

                             &conf->stapling_responder, conf->stapling_verify)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_ssl_srv_conf_t *sscf = conf;

    char  *rv;

    rv = ngx_conf_set_flag_slot(cf, cmd, conf);

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    sscf->file = cf->conf_file->file.name.data;

    sscf->line = cf->conf_file->line;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_ssl_srv_conf_t *sscf = conf;

    ngx_str_t  *value;

    if (sscf->passwords != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);

    if (sscf->passwords == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_ssl_srv_conf_t *sscf = conf;

    size_t       len;

    ngx_str_t   *value, name, size;

    ngx_int_t    n;

    ngx_uint_t   i, j;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "off") == 0) {

            sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;

            continue;

        }

        if (ngx_strcmp(value[i].data, "none") == 0) {

            sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;

            continue;

        }

        if (ngx_strcmp(value[i].data, "builtin") == 0) {

            sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;

            continue;

        }

        if (value[i].len > sizeof("builtin:") - 1

            && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)

               == 0)

        {

            n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,

                         value[i].len - (sizeof("builtin:") - 1));

            if (n == NGX_ERROR) {

                goto invalid;

            }

            sscf->builtin_session_cache = n;

            continue;

        }

        if (value[i].len > sizeof("shared:") - 1

            && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)

               == 0)

        {

            len = 0;

            for (j = sizeof("shared:") - 1; j < value[i].len; j++) {

                if (value[i].data[j] == ':') {

                    break;

                }

                len++;

            }

            if (len == 0) {

                goto invalid;

            }

            name.len = len;

            name.data = value[i].data + sizeof("shared:") - 1;

            size.len = value[i].len - j - 1;

            size.data = name.data + len + 1;

            n = ngx_parse_size(&size);

            if (n == NGX_ERROR) {

                goto invalid;

            }

            if (n < (ngx_int_t) (8 * ngx_pagesize)) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "session cache \"%V\" is too small",

                                   &value[i]);

                return NGX_CONF_ERROR;

            }

            sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,

                                                   &ngx_http_ssl_module);

            if (sscf->shm_zone == NULL) {

                return NGX_CONF_ERROR;

            }

            sscf->shm_zone->init = ngx_ssl_session_cache_init;

            continue;

        }

        goto invalid;

    }

    if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {

        sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;

    }

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid session cache \"%V\"", &value[i]);

    return NGX_CONF_ERROR;

}

static ngx_int_t

‌ngx_http_ssl_init(ngx_conf_t *cf)

{

    ngx_uint_t                   s;

    ngx_http_ssl_srv_conf_t     *sscf;

    ngx_http_core_loc_conf_t    *clcf;

    ngx_http_core_srv_conf_t   **cscfp;

    ngx_http_core_main_conf_t   *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    cscfp = cmcf->servers.elts;

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

        sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];

        if (sscf->ssl.ctx == NULL || !sscf->stapling) {

            continue;

        }

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

        if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,

                                      clcf->resolver_timeout)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

src/http/ngx_http_spdy.h - nginx-1.7.9

Data types defined

ngx_http_spdy_connection_s

ngx_http_spdy_connection_t

ngx_http_spdy_handler_pt

ngx_http_spdy_out_frame_s

ngx_http_spdy_out_frame_t

ngx_http_spdy_stream_s

Functions defined

ngx_http_spdy_queue_blocked_frame

ngx_http_spdy_queue_frame

Macros defined

NGX_SPDY_CTL_BIT

NGX_SPDY_DATA_DISCARD

NGX_SPDY_DATA_ERROR

NGX_SPDY_DATA_INTERNAL_ERROR

NGX_SPDY_DELTA_SIZE

NGX_SPDY_FLAG_CLEAR_SETTINGS

NGX_SPDY_FLAG_FIN

NGX_SPDY_FLAG_UNIDIRECTIONAL

NGX_SPDY_FRAME_HEADER_SIZE

NGX_SPDY_GOAWAY

NGX_SPDY_GOAWAY_SIZE

NGX_SPDY_HEADERS

NGX_SPDY_HIGHEST_PRIORITY

NGX_SPDY_LOWEST_PRIORITY

NGX_SPDY_MAX_FRAME_SIZE

NGX_SPDY_NPN_ADVERTISE

NGX_SPDY_NPN_NEGOTIATED

NGX_SPDY_NV_NLEN_SIZE

NGX_SPDY_NV_NUM_SIZE

NGX_SPDY_NV_VLEN_SIZE

NGX_SPDY_PING

NGX_SPDY_PING_SIZE

NGX_SPDY_RST_STREAM

NGX_SPDY_RST_STREAM_SIZE

NGX_SPDY_SETTINGS

NGX_SPDY_SETTINGS_FID_SIZE

NGX_SPDY_SETTINGS_NUM_SIZE

NGX_SPDY_SETTINGS_PAIR_SIZE

NGX_SPDY_SETTINGS_VAL_SIZE

NGX_SPDY_SID_SIZE

NGX_SPDY_STATE_BUFFER_SIZE

NGX_SPDY_SYN_REPLY

NGX_SPDY_SYN_REPLY_SIZE

NGX_SPDY_SYN_STREAM

NGX_SPDY_SYN_STREAM_SIZE

NGX_SPDY_VERSION

NGX_SPDY_WINDOW_UPDATE

NGX_SPDY_WINDOW_UPDATE_SIZE

_NGX_HTTP_SPDY_H_INCLUDED_

ngx_spdy_ctl_frame_head

ngx_spdy_frame_aligned_write_uint16

ngx_spdy_frame_aligned_write_uint32

ngx_spdy_frame_write_flags_and_id

ngx_spdy_frame_write_flags_and_len

ngx_spdy_frame_write_head

ngx_spdy_frame_write_sid

ngx_spdy_frame_write_uint16

ngx_spdy_frame_write_uint16

ngx_spdy_frame_write_uint32

ngx_spdy_frame_write_uint32

ngx_spdy_frame_write_window

Source code

/*

* Copyright (C) Nginx, Inc.

* Copyright (C) Valentin V. Bartenev

*/

#ifndef _NGX_HTTP_SPDY_H_INCLUDED_

‌#define _NGX_HTTP_SPDY_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <zlib.h>

‌#define NGX_SPDY_VERSION              3

‌#define NGX_SPDY_NPN_ADVERTISE        "\x08spdy/3.1"

‌#define NGX_SPDY_NPN_NEGOTIATED       "spdy/3.1"

‌#define NGX_SPDY_STATE_BUFFER_SIZE    16

‌#define NGX_SPDY_CTL_BIT              1

‌#define NGX_SPDY_SYN_STREAM           1

‌#define NGX_SPDY_SYN_REPLY            2

‌#define NGX_SPDY_RST_STREAM           3

‌#define NGX_SPDY_SETTINGS             4

‌#define NGX_SPDY_PING                 6

‌#define NGX_SPDY_GOAWAY               7

‌#define NGX_SPDY_HEADERS              8

‌#define NGX_SPDY_WINDOW_UPDATE        9

‌#define NGX_SPDY_FRAME_HEADER_SIZE    8

‌#define NGX_SPDY_SID_SIZE             4

‌#define NGX_SPDY_DELTA_SIZE           4

‌#define NGX_SPDY_SYN_STREAM_SIZE      10

‌#define NGX_SPDY_SYN_REPLY_SIZE       4

‌#define NGX_SPDY_RST_STREAM_SIZE      8

‌#define NGX_SPDY_PING_SIZE            4

‌#define NGX_SPDY_GOAWAY_SIZE          8

‌#define NGX_SPDY_WINDOW_UPDATE_SIZE   8

‌#define NGX_SPDY_NV_NUM_SIZE          4

‌#define NGX_SPDY_NV_NLEN_SIZE         4

‌#define NGX_SPDY_NV_VLEN_SIZE         4

‌#define NGX_SPDY_SETTINGS_NUM_SIZE    4

‌#define NGX_SPDY_SETTINGS_FID_SIZE    4

‌#define NGX_SPDY_SETTINGS_VAL_SIZE    4

‌#define NGX_SPDY_SETTINGS_PAIR_SIZE                                           \

    (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)

‌#define NGX_SPDY_HIGHEST_PRIORITY     0

‌#define NGX_SPDY_LOWEST_PRIORITY      7

‌#define NGX_SPDY_FLAG_FIN             0x01

‌#define NGX_SPDY_FLAG_UNIDIRECTIONAL  0x02

‌#define NGX_SPDY_FLAG_CLEAR_SETTINGS  0x01

‌#define NGX_SPDY_MAX_FRAME_SIZE       ((1 << 24) - 1)

‌#define NGX_SPDY_DATA_DISCARD         1

‌#define NGX_SPDY_DATA_ERROR           2

‌#define NGX_SPDY_DATA_INTERNAL_ERROR  3

‌typedef struct ngx_http_spdy_connection_s   ngx_http_spdy_connection_t;

‌typedef struct ngx_http_spdy_out_frame_s    ngx_http_spdy_out_frame_t;

‌typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,

    u_char *pos, u_char *end);

‌struct ngx_http_spdy_connection_s {

    ngx_connection_t                *connection;

    ngx_http_connection_t           *http_connection;

    ngx_uint_t                       processing;

    size_t                           send_window;

    size_t                           recv_window;

    size_t                           init_window;

    ngx_queue_t                      waiting;

    u_char                           buffer[NGX_SPDY_STATE_BUFFER_SIZE];

    size_t                           buffer_used;

    ngx_http_spdy_handler_pt         handler;

    z_stream                         zstream_in;

    z_stream                         zstream_out;

    ngx_pool_t                      *pool;

    ngx_http_spdy_out_frame_t       *free_ctl_frames;

    ngx_connection_t                *free_fake_connections;

    ngx_http_spdy_stream_t         **streams_index;

    ngx_http_spdy_out_frame_t       *last_out;

    ngx_queue_t                      posted;

    ngx_http_spdy_stream_t          *stream;

    ngx_uint_t                       entries;

    size_t                           length;

    u_char                           flags;

    ngx_uint_t                       last_sid;

    unsigned                         blocked:1;

    unsigned                         incomplete:1;

};

‌struct ngx_http_spdy_stream_s {

    ngx_uint_t                       id;

    ngx_http_request_t              *request;

    ngx_http_spdy_connection_t      *connection;

    ngx_http_spdy_stream_t          *index;

    ngx_uint_t                       header_buffers;

    ngx_uint_t                       queued;

    /*

     * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the

     * send_window to become negative, hence it's signed.

     */

    ssize_t                          send_window;

    size_t                           recv_window;

    ngx_http_spdy_out_frame_t       *free_frames;

    ngx_chain_t                     *free_data_headers;

    ngx_chain_t                     *free_bufs;

    ngx_queue_t                      queue;

    unsigned                         priority:3;

    unsigned                         handled:1;

    unsigned                         blocked:1;

    unsigned                         exhausted:1;

    unsigned                         in_closed:1;

    unsigned                         out_closed:1;

    unsigned                         skip_data:2;

};

‌struct ngx_http_spdy_out_frame_s {

    ngx_http_spdy_out_frame_t       *next;

    ngx_chain_t                     *first;

    ngx_chain_t                     *last;

    ngx_int_t                      (*handler)(ngx_http_spdy_connection_t *sc,

                                        ngx_http_spdy_out_frame_t *frame);

    ngx_http_spdy_stream_t          *stream;

    size_t                           length;

    ngx_uint_t                       priority;

    unsigned                         blocked:1;

    unsigned                         fin:1;

};

static ngx_inline void

‌ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_http_spdy_out_frame_t  **out;

    for (out = &sc->last_out; *out; out = &(*out)->next)

    {

        /*

         * NB: higher values represent lower priorities.

         */

        if (frame->priority >= (*out)->priority) {

            break;

        }

    }

    frame->next = *out;

    *out = frame;

}

static ngx_inline void

‌ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_http_spdy_out_frame_t  **out;

    for (out = &sc->last_out; *out; out = &(*out)->next)

    {

        if ((*out)->blocked) {

            break;

        }

    }

    frame->next = *out;

    *out = frame;

}

void ngx_http_spdy_init(ngx_event_t *rev);

void ngx_http_spdy_request_headers_init(void);

ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,

    ngx_http_client_body_handler_pt post_handler);

void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);

ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);

‌#define ngx_spdy_frame_aligned_write_uint16(p, s)                             \

    (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))

‌#define ngx_spdy_frame_aligned_write_uint32(p, s)                             \

    (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))

#if (NGX_HAVE_NONALIGNED)

‌#define ngx_spdy_frame_write_uint16  ngx_spdy_frame_aligned_write_uint16

‌#define ngx_spdy_frame_write_uint32  ngx_spdy_frame_aligned_write_uint32

#else

‌#define ngx_spdy_frame_write_uint16(p, s)                                     \

    ((p)[0] = (u_char) ((s) >> 8),                                            \

     (p)[1] = (u_char)  (s),                                                  \

     (p) + sizeof(uint16_t))

‌#define ngx_spdy_frame_write_uint32(p, s)                                     \

    ((p)[0] = (u_char) ((s) >> 24),                                           \

     (p)[1] = (u_char) ((s) >> 16),                                           \

     (p)[2] = (u_char) ((s) >> 8),                                            \

     (p)[3] = (u_char)  (s),                                                  \

     (p) + sizeof(uint32_t))

#endif

‌#define ngx_spdy_ctl_frame_head(t)                                            \

    ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))

‌#define ngx_spdy_frame_write_head(p, t)                                       \

    ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))

‌#define ngx_spdy_frame_write_flags_and_len(p, f, l)                           \

    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))

‌#define ngx_spdy_frame_write_flags_and_id(p, f, i)                            \

    ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i))

‌#define ngx_spdy_frame_write_sid     ngx_spdy_frame_aligned_write_uint32

‌#define ngx_spdy_frame_write_window  ngx_spdy_frame_aligned_write_uint32

#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */

src/mail/ngx_mail_ssl_module.c - nginx-1.7.9

Global variables defined

ngx_mail_ssl_commands

ngx_mail_ssl_module

ngx_mail_ssl_module_ctx

ngx_mail_ssl_protocols

ngx_mail_ssl_sess_id_ctx

ngx_mail_starttls_state

Functions defined

ngx_mail_ssl_create_conf

ngx_mail_ssl_enable

ngx_mail_ssl_merge_conf

ngx_mail_ssl_password_file

ngx_mail_ssl_session_cache

ngx_mail_ssl_starttls

Macros defined

NGX_DEFAULT_CIPHERS

NGX_DEFAULT_ECDH_CURVE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_mail.h>

‌#define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"

‌#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"

static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);

static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);

static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_conf_enum_t  ngx_mail_starttls_state[] = {

    { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },

    { ngx_string("on"), NGX_MAIL_STARTTLS_ON },

    { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY },

    { ngx_null_string, 0 }

};

‌static ngx_conf_bitmask_t  ngx_mail_ssl_protocols[] = {

    { ngx_string("SSLv2"), NGX_SSL_SSLv2 },

    { ngx_string("SSLv3"), NGX_SSL_SSLv3 },

    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },

    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },

    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_mail_ssl_commands[] = {

    { ngx_string("ssl"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_mail_ssl_enable,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, enable),

      NULL },

    { ngx_string("starttls"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_mail_ssl_starttls,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, starttls),

      ngx_mail_starttls_state },

    { ngx_string("ssl_certificate"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, certificate),

      NULL },

    { ngx_string("ssl_certificate_key"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, certificate_key),

      NULL },

    { ngx_string("ssl_password_file"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_mail_ssl_password_file,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ssl_dhparam"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, dhparam),

      NULL },

    { ngx_string("ssl_ecdh_curve"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, ecdh_curve),

      NULL },

    { ngx_string("ssl_protocols"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, protocols),

      &ngx_mail_ssl_protocols },

    { ngx_string("ssl_ciphers"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, ciphers),

      NULL },

    { ngx_string("ssl_prefer_server_ciphers"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),

      NULL },

    { ngx_string("ssl_session_cache"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,

      ngx_mail_ssl_session_cache,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ssl_session_tickets"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, session_tickets),

      NULL },

    { ngx_string("ssl_session_ticket_key"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, session_ticket_keys),

      NULL },

    { ngx_string("ssl_session_timeout"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_sec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_ssl_conf_t, session_timeout),

      NULL },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_ssl_module_ctx = {

    NULL,                                  /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_ssl_create_conf,              /* create server configuration */

    ngx_mail_ssl_merge_conf                /* merge server configuration */

};

‌ngx_module_t  ngx_mail_ssl_module = {

    NGX_MODULE_V1,

    &ngx_mail_ssl_module_ctx,              /* module context */

    ngx_mail_ssl_commands,                 /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL");

static void *

‌ngx_mail_ssl_create_conf(ngx_conf_t *cf)

{

    ngx_mail_ssl_conf_t  *scf;

    scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));

    if (scf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     scf->protocols = 0;

     *     scf->certificate = { 0, NULL };

     *     scf->certificate_key = { 0, NULL };

     *     scf->dhparam = { 0, NULL };

     *     scf->ecdh_curve = { 0, NULL };

     *     scf->ciphers = { 0, NULL };

     *     scf->shm_zone = NULL;

     */

    scf->enable = NGX_CONF_UNSET;

    scf->starttls = NGX_CONF_UNSET_UINT;

    scf->passwords = NGX_CONF_UNSET_PTR;

    scf->prefer_server_ciphers = NGX_CONF_UNSET;

    scf->builtin_session_cache = NGX_CONF_UNSET;

    scf->session_timeout = NGX_CONF_UNSET;

    scf->session_tickets = NGX_CONF_UNSET;

    scf->session_ticket_keys = NGX_CONF_UNSET_PTR;

    return scf;

}

static char *

‌ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_ssl_conf_t *prev = parent;

    ngx_mail_ssl_conf_t *conf = child;

    char                *mode;

    ngx_pool_cleanup_t  *cln;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_uint_value(conf->starttls, prev->starttls,

                         NGX_MAIL_STARTTLS_OFF);

    ngx_conf_merge_value(conf->session_timeout,

                         prev->session_timeout, 300);

    ngx_conf_merge_value(conf->prefer_server_ciphers,

                         prev->prefer_server_ciphers, 0);

    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,

                         (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1

                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));

    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");

    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");

    ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);

    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");

    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,

                         NGX_DEFAULT_ECDH_CURVE);

    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);

    conf->ssl.log = cf->log;

    if (conf->enable) {

       mode = "ssl";

    } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {

       mode = "starttls";

    } else {

       mode = "";

    }

    if (conf->file == NULL) {

        conf->file = prev->file;

        conf->line = prev->line;

    }

    if (*mode) {

        if (conf->certificate.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate\" is defined for "

                          "the \"%s\" directive in %s:%ui",

                          mode, conf->file, conf->line);

            return NGX_CONF_ERROR;

        }

        if (conf->certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate_key\" is defined for "

                          "the \"%s\" directive in %s:%ui",

                          mode, conf->file, conf->line);

            return NGX_CONF_ERROR;

        }

    } else {

        if (conf->certificate.len == 0) {

            return NGX_CONF_OK;

        }

        if (conf->certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"ssl_certificate_key\" is defined "

                          "for certificate \"%V\"",

                          &conf->certificate);

            return NGX_CONF_ERROR;

        }

    }

    if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_CONF_ERROR;

    }

    cln->handler = ngx_ssl_cleanup_ctx;

    cln->data = &conf->ssl;

    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,

                            &conf->certificate_key, conf->passwords)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    if (SSL_CTX_set_cipher_list(conf->ssl.ctx,

                                (const char *) conf->ciphers.data)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,

                      "SSL_CTX_set_cipher_list(\"%V\") failed",

                      &conf->ciphers);

        return NGX_CONF_ERROR;

    }

    if (conf->prefer_server_ciphers) {

        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);

    }

    SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);

    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->builtin_session_cache,

                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);

    if (conf->shm_zone == NULL) {

        conf->shm_zone = prev->shm_zone;

    }

    if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,

                              conf->builtin_session_cache,

                              conf->shm_zone, conf->session_timeout)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->session_tickets,

                         prev->session_tickets, 1);

#ifdef SSL_OP_NO_TICKET

    if (!conf->session_tickets) {

        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);

    }

#endif

    ngx_conf_merge_ptr_value(conf->session_ticket_keys,

                         prev->session_ticket_keys, NULL);

    if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_ssl_conf_t  *scf = conf;

    char  *rv;

    rv = ngx_conf_set_flag_slot(cf, cmd, conf);

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "\"starttls\" directive conflicts with \"ssl on\"");

        return NGX_CONF_ERROR;

    }

    scf->file = cf->conf_file->file.name.data;

    scf->line = cf->conf_file->line;

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_ssl_conf_t  *scf = conf;

    char  *rv;

    rv = ngx_conf_set_enum_slot(cf, cmd, conf);

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "\"ssl\" directive conflicts with \"starttls\"");

        return NGX_CONF_ERROR;

    }

    scf->file = cf->conf_file->file.name.data;

    scf->line = cf->conf_file->line;

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_ssl_conf_t  *scf = conf;

    ngx_str_t  *value;

    if (scf->passwords != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);

    if (scf->passwords == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_ssl_conf_t  *scf = conf;

    size_t       len;

    ngx_str_t   *value, name, size;

    ngx_int_t    n;

    ngx_uint_t   i, j;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "off") == 0) {

            scf->builtin_session_cache = NGX_SSL_NO_SCACHE;

            continue;

        }

        if (ngx_strcmp(value[i].data, "none") == 0) {

            scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;

            continue;

        }

        if (ngx_strcmp(value[i].data, "builtin") == 0) {

            scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;

            continue;

        }

        if (value[i].len > sizeof("builtin:") - 1

            && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)

               == 0)

        {

            n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,

                         value[i].len - (sizeof("builtin:") - 1));

            if (n == NGX_ERROR) {

                goto invalid;

            }

            scf->builtin_session_cache = n;

            continue;

        }

        if (value[i].len > sizeof("shared:") - 1

            && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)

               == 0)

        {

            len = 0;

            for (j = sizeof("shared:") - 1; j < value[i].len; j++) {

                if (value[i].data[j] == ':') {

                    break;

                }

                len++;

            }

            if (len == 0) {

                goto invalid;

            }

            name.len = len;

            name.data = value[i].data + sizeof("shared:") - 1;

            size.len = value[i].len - j - 1;

            size.data = name.data + len + 1;

            n = ngx_parse_size(&size);

            if (n == NGX_ERROR) {

                goto invalid;

            }

            if (n < (ngx_int_t) (8 * ngx_pagesize)) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "session cache \"%V\" is too small",

                                   &value[i]);

                return NGX_CONF_ERROR;

            }

            scf->shm_zone = ngx_shared_memory_add(cf, &name, n,

                                                   &ngx_mail_ssl_module);

            if (scf->shm_zone == NULL) {

                return NGX_CONF_ERROR;

            }

            scf->shm_zone->init = ngx_ssl_session_cache_init;

            continue;

        }

        goto invalid;

    }

    if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {

        scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;

    }

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid session cache \"%V\"", &value[i]);

    return NGX_CONF_ERROR;

}

src/mail/ngx_mail_ssl_module.h - nginx-1.7.9

Data types defined

ngx_mail_ssl_conf_t

Macros defined

NGX_MAIL_STARTTLS_OFF

NGX_MAIL_STARTTLS_ON

NGX_MAIL_STARTTLS_ONLY

_NGX_MAIL_SSL_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MAIL_SSL_H_INCLUDED_

‌#define _NGX_MAIL_SSL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_mail.h>

‌#define NGX_MAIL_STARTTLS_OFF   0

‌#define NGX_MAIL_STARTTLS_ON    1

‌#define NGX_MAIL_STARTTLS_ONLY  2

typedef struct {

    ngx_flag_t       enable;

    ngx_flag_t       prefer_server_ciphers;

    ngx_ssl_t        ssl;

    ngx_uint_t       starttls;

    ngx_uint_t       protocols;

    ssize_t          builtin_session_cache;

    time_t           session_timeout;

    ngx_str_t        certificate;

    ngx_str_t        certificate_key;

    ngx_str_t        dhparam;

    ngx_str_t        ecdh_curve;

    ngx_str_t        ciphers;

    ngx_array_t     *passwords;

    ngx_shm_zone_t  *shm_zone;

    ngx_flag_t       session_tickets;

    ngx_array_t     *session_ticket_keys;

    u_char          *file;

    ngx_uint_t       line;

‌} ngx_mail_ssl_conf_t;

extern ngx_module_t  ngx_mail_ssl_module;

#endif /* _NGX_MAIL_SSL_H_INCLUDED_ */

src/mail/ngx_mail.h - nginx-1.7.9

Data types defined

ngx_imap_state_e

ngx_mail_addr_conf_t

ngx_mail_auth_state_pt

ngx_mail_conf_addr_t

ngx_mail_conf_ctx_t

ngx_mail_conf_port_t

ngx_mail_core_main_conf_t

ngx_mail_core_srv_conf_t

ngx_mail_in6_addr_t

ngx_mail_in_addr_t

ngx_mail_init_protocol_pt

ngx_mail_init_session_pt

ngx_mail_listen_t

ngx_mail_log_ctx_t

ngx_mail_module_t

ngx_mail_parse_command_pt

ngx_mail_port_t

ngx_mail_protocol_s

ngx_mail_protocol_t

ngx_mail_proxy_ctx_t

ngx_mail_session_t

ngx_pop3_state_e

ngx_smtp_state_e

Macros defined

NGX_IMAP_AUTHENTICATE

NGX_IMAP_CAPABILITY

NGX_IMAP_LOGIN

NGX_IMAP_LOGOUT

NGX_IMAP_NEXT

NGX_IMAP_NOOP

NGX_IMAP_STARTTLS

NGX_MAIL_AUTH_APOP

NGX_MAIL_AUTH_APOP_ENABLED

NGX_MAIL_AUTH_CRAM_MD5

NGX_MAIL_AUTH_CRAM_MD5_ENABLED

NGX_MAIL_AUTH_LOGIN

NGX_MAIL_AUTH_LOGIN_ENABLED

NGX_MAIL_AUTH_LOGIN_USERNAME

NGX_MAIL_AUTH_NONE

NGX_MAIL_AUTH_NONE_ENABLED

NGX_MAIL_AUTH_PLAIN

NGX_MAIL_AUTH_PLAIN_ENABLED

NGX_MAIL_IMAP_PROTOCOL

NGX_MAIL_MAIN_CONF

NGX_MAIL_MAIN_CONF_OFFSET

NGX_MAIL_MODULE

NGX_MAIL_PARSE_INVALID_COMMAND

NGX_MAIL_POP3_PROTOCOL

NGX_MAIL_SMTP_PROTOCOL

NGX_MAIL_SRV_CONF

NGX_MAIL_SRV_CONF_OFFSET

NGX_POP3_APOP

NGX_POP3_AUTH

NGX_POP3_CAPA

NGX_POP3_DELE

NGX_POP3_LIST

NGX_POP3_NOOP

NGX_POP3_PASS

NGX_POP3_QUIT

NGX_POP3_RETR

NGX_POP3_RSET

NGX_POP3_STAT

NGX_POP3_STLS

NGX_POP3_TOP

NGX_POP3_UIDL

NGX_POP3_USER

NGX_SMTP_AUTH

NGX_SMTP_DATA

NGX_SMTP_EHLO

NGX_SMTP_EXPN

NGX_SMTP_HELO

NGX_SMTP_HELP

NGX_SMTP_MAIL

NGX_SMTP_NOOP

NGX_SMTP_QUIT

NGX_SMTP_RCPT

NGX_SMTP_RSET

NGX_SMTP_STARTTLS

NGX_SMTP_VRFY

_NGX_MAIL_H_INCLUDED_

ngx_mail_conf_get_module_main_conf

ngx_mail_conf_get_module_srv_conf

ngx_mail_delete_ctx

ngx_mail_get_module_ctx

ngx_mail_get_module_main_conf

ngx_mail_get_module_srv_conf

ngx_mail_set_ctx

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MAIL_H_INCLUDED_

‌#define _NGX_MAIL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

#if (NGX_MAIL_SSL)

#include <ngx_mail_ssl_module.h>

#endif

typedef struct {

    void                  **main_conf;

    void                  **srv_conf;

‌} ngx_mail_conf_ctx_t;

typedef struct {

    u_char                  sockaddr[NGX_SOCKADDRLEN];

    socklen_t               socklen;

    /* server ctx */

    ngx_mail_conf_ctx_t    *ctx;

    unsigned                bind:1;

    unsigned                wildcard:1;

#if (NGX_MAIL_SSL)

    unsigned                ssl:1;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    unsigned                ipv6only:1;

#endif

    unsigned                so_keepalive:2;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    int                     tcp_keepidle;

    int                     tcp_keepintvl;

    int                     tcp_keepcnt;

#endif

‌} ngx_mail_listen_t;

typedef struct {

    ngx_mail_conf_ctx_t    *ctx;

    ngx_str_t               addr_text;

#if (NGX_MAIL_SSL)

    ngx_uint_t              ssl;    /* unsigned   ssl:1; */

#endif

‌} ngx_mail_addr_conf_t;

typedef struct {

    in_addr_t               addr;

    ngx_mail_addr_conf_t    conf;

‌} ngx_mail_in_addr_t;

#if (NGX_HAVE_INET6)

typedef struct {

    struct in6_addr         addr6;

    ngx_mail_addr_conf_t    conf;

‌} ngx_mail_in6_addr_t;

#endif

typedef struct {

    /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */

    void                   *addrs;

    ngx_uint_t              naddrs;

‌} ngx_mail_port_t;

typedef struct {

    int                     family;

    in_port_t               port;

    ngx_array_t             addrs;       /* array of ngx_mail_conf_addr_t */

‌} ngx_mail_conf_port_t;

typedef struct {

    struct sockaddr        *sockaddr;

    socklen_t               socklen;

    ngx_mail_conf_ctx_t    *ctx;

    unsigned                bind:1;

    unsigned                wildcard:1;

#if (NGX_MAIL_SSL)

    unsigned                ssl:1;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    unsigned                ipv6only:1;

#endif

    unsigned                so_keepalive:2;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    int                     tcp_keepidle;

    int                     tcp_keepintvl;

    int                     tcp_keepcnt;

#endif

‌} ngx_mail_conf_addr_t;

typedef struct {

    ngx_array_t             servers;     /* ngx_mail_core_srv_conf_t */

    ngx_array_t             listen;      /* ngx_mail_listen_t */

‌} ngx_mail_core_main_conf_t;

‌#define NGX_MAIL_POP3_PROTOCOL  0

‌#define NGX_MAIL_IMAP_PROTOCOL  1

‌#define NGX_MAIL_SMTP_PROTOCOL  2

‌typedef struct ngx_mail_protocol_s  ngx_mail_protocol_t;

typedef struct {

    ngx_mail_protocol_t    *protocol;

    ngx_msec_t              timeout;

    ngx_msec_t              resolver_timeout;

    ngx_flag_t              so_keepalive;

    ngx_str_t               server_name;

    u_char                 *file_name;

    ngx_int_t               line;

    ngx_resolver_t         *resolver;

    /* server ctx */

    ngx_mail_conf_ctx_t    *ctx;

‌} ngx_mail_core_srv_conf_t;

typedef enum {

    ngx_pop3_start = 0,

    ngx_pop3_user,

    ngx_pop3_passwd,

    ngx_pop3_auth_login_username,

    ngx_pop3_auth_login_password,

    ngx_pop3_auth_plain,

    ngx_pop3_auth_cram_md5

‌} ngx_pop3_state_e;

typedef enum {

    ngx_imap_start = 0,

    ngx_imap_auth_login_username,

    ngx_imap_auth_login_password,

    ngx_imap_auth_plain,

    ngx_imap_auth_cram_md5,

    ngx_imap_login,

    ngx_imap_user,

    ngx_imap_passwd

‌} ngx_imap_state_e;

typedef enum {

    ngx_smtp_start = 0,

    ngx_smtp_auth_login_username,

    ngx_smtp_auth_login_password,

    ngx_smtp_auth_plain,

    ngx_smtp_auth_cram_md5,

    ngx_smtp_helo,

    ngx_smtp_helo_xclient,

    ngx_smtp_helo_from,

    ngx_smtp_xclient,

    ngx_smtp_xclient_from,

    ngx_smtp_xclient_helo,

    ngx_smtp_from,

    ngx_smtp_to

‌} ngx_smtp_state_e;

typedef struct {

    ngx_peer_connection_t   upstream;

    ngx_buf_t              *buffer;

‌} ngx_mail_proxy_ctx_t;

typedef struct {

    uint32_t                signature;         /* "MAIL" */

    ngx_connection_t       *connection;

    ngx_str_t               out;

    ngx_buf_t              *buffer;

    void                  **ctx;

    void                  **main_conf;

    void                  **srv_conf;

    ngx_resolver_ctx_t     *resolver_ctx;

    ngx_mail_proxy_ctx_t   *proxy;

    ngx_uint_t              mail_state;

    unsigned                protocol:3;

    unsigned                blocked:1;

    unsigned                quit:1;

    unsigned                quoted:1;

    unsigned                backslash:1;

    unsigned                no_sync_literal:1;

    unsigned                starttls:1;

    unsigned                esmtp:1;

    unsigned                auth_method:3;

    unsigned                auth_wait:1;

    ngx_str_t               login;

    ngx_str_t               passwd;

    ngx_str_t               salt;

    ngx_str_t               tag;

    ngx_str_t               tagged_line;

    ngx_str_t               text;

    ngx_str_t              *addr_text;

    ngx_str_t               host;

    ngx_str_t               smtp_helo;

    ngx_str_t               smtp_from;

    ngx_str_t               smtp_to;

    ngx_str_t               cmd;

    ngx_uint_t              command;

    ngx_array_t             args;

    ngx_uint_t              login_attempt;

    /* used to parse POP3/IMAP/SMTP command */

    ngx_uint_t              state;

    u_char                 *cmd_start;

    u_char                 *arg_start;

    u_char                 *arg_end;

    ngx_uint_t              literal_len;

‌} ngx_mail_session_t;

typedef struct {

    ngx_str_t              *client;

    ngx_mail_session_t     *session;

‌} ngx_mail_log_ctx_t;

‌#define NGX_POP3_USER          1

‌#define NGX_POP3_PASS          2

‌#define NGX_POP3_CAPA          3

‌#define NGX_POP3_QUIT          4

‌#define NGX_POP3_NOOP          5

‌#define NGX_POP3_STLS          6

‌#define NGX_POP3_APOP          7

‌#define NGX_POP3_AUTH          8

‌#define NGX_POP3_STAT          9

‌#define NGX_POP3_LIST          10

‌#define NGX_POP3_RETR          11

‌#define NGX_POP3_DELE          12

‌#define NGX_POP3_RSET          13

‌#define NGX_POP3_TOP           14

‌#define NGX_POP3_UIDL          15

‌#define NGX_IMAP_LOGIN         1

‌#define NGX_IMAP_LOGOUT        2

‌#define NGX_IMAP_CAPABILITY    3

‌#define NGX_IMAP_NOOP          4

‌#define NGX_IMAP_STARTTLS      5

‌#define NGX_IMAP_NEXT          6

‌#define NGX_IMAP_AUTHENTICATE  7

‌#define NGX_SMTP_HELO          1

‌#define NGX_SMTP_EHLO          2

‌#define NGX_SMTP_AUTH          3

‌#define NGX_SMTP_QUIT          4

‌#define NGX_SMTP_NOOP          5

‌#define NGX_SMTP_MAIL          6

‌#define NGX_SMTP_RSET          7

‌#define NGX_SMTP_RCPT          8

‌#define NGX_SMTP_DATA          9

‌#define NGX_SMTP_VRFY          10

‌#define NGX_SMTP_EXPN          11

‌#define NGX_SMTP_HELP          12

‌#define NGX_SMTP_STARTTLS      13

‌#define NGX_MAIL_AUTH_PLAIN             0

‌#define NGX_MAIL_AUTH_LOGIN             1

‌#define NGX_MAIL_AUTH_LOGIN_USERNAME    2

‌#define NGX_MAIL_AUTH_APOP              3

‌#define NGX_MAIL_AUTH_CRAM_MD5          4

‌#define NGX_MAIL_AUTH_NONE              5

‌#define NGX_MAIL_AUTH_PLAIN_ENABLED     0x0002

‌#define NGX_MAIL_AUTH_LOGIN_ENABLED     0x0004

‌#define NGX_MAIL_AUTH_APOP_ENABLED      0x0008

‌#define NGX_MAIL_AUTH_CRAM_MD5_ENABLED  0x0010

‌#define NGX_MAIL_AUTH_NONE_ENABLED      0x0020

‌#define NGX_MAIL_PARSE_INVALID_COMMAND  20

‌typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,

    ngx_connection_t *c);

‌typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev);

‌typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev);

‌typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s);

‌struct ngx_mail_protocol_s {

    ngx_str_t                   name;

    in_port_t                   port[4];

    ngx_uint_t                  type;

    ngx_mail_init_session_pt    init_session;

    ngx_mail_init_protocol_pt   init_protocol;

    ngx_mail_parse_command_pt   parse_command;

    ngx_mail_auth_state_pt      auth_state;

    ngx_str_t                   internal_server_error;

};

typedef struct {

    ngx_mail_protocol_t        *protocol;

    void                       *(*create_main_conf)(ngx_conf_t *cf);

    char                       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void                       *(*create_srv_conf)(ngx_conf_t *cf);

    char                       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,

                                      void *conf);

‌} ngx_mail_module_t;

‌#define NGX_MAIL_MODULE         0x4C49414D     /* "MAIL" */

‌#define NGX_MAIL_MAIN_CONF      0x02000000

‌#define NGX_MAIL_SRV_CONF       0x04000000

‌#define NGX_MAIL_MAIN_CONF_OFFSET  offsetof(ngx_mail_conf_ctx_t, main_conf)

‌#define NGX_MAIL_SRV_CONF_OFFSET   offsetof(ngx_mail_conf_ctx_t, srv_conf)

‌#define ngx_mail_get_module_ctx(s, module)     (s)->ctx[module.ctx_index]

‌#define ngx_mail_set_ctx(s, c, module)         s->ctx[module.ctx_index] = c;

‌#define ngx_mail_delete_ctx(s, module)         s->ctx[module.ctx_index] = NULL;

‌#define ngx_mail_get_module_main_conf(s, module)                             \

    (s)->main_conf[module.ctx_index]

‌#define ngx_mail_get_module_srv_conf(s, module)  (s)->srv_conf[module.ctx_index]

‌#define ngx_mail_conf_get_module_main_conf(cf, module)                       \

    ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]

‌#define ngx_mail_conf_get_module_srv_conf(cf, module)                        \

    ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]

#if (NGX_MAIL_SSL)

void ngx_mail_starttls_handler(ngx_event_t *rev);

ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c);

#endif

void ngx_mail_init_connection(ngx_connection_t *c);

ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,

    ngx_mail_core_srv_conf_t *cscf);

ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c,

    ngx_uint_t n);

ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s,

    ngx_connection_t *c, ngx_uint_t n);

ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,

    ngx_connection_t *c);

ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,

    ngx_connection_t *c, char *prefix, size_t len);

ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);

ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_send(ngx_event_t *wev);

ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_close_connection(ngx_connection_t *c);

void ngx_mail_session_internal_server_error(ngx_mail_session_t *s);

u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len);

char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

/* STUB */

void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);

void ngx_mail_auth_http_init(ngx_mail_session_t *s);

/**/

extern ngx_uint_t    ngx_mail_max_module;

extern ngx_module_t  ngx_mail_core_module;

#endif /* _NGX_MAIL_H_INCLUDED_ */

src/mail/ngx_mail_handler.c - nginx-1.7.9

Functions defined

ngx_mail_auth

ngx_mail_auth_cram_md5

ngx_mail_auth_cram_md5_salt

ngx_mail_auth_login_password

ngx_mail_auth_login_username

ngx_mail_auth_plain

ngx_mail_close_connection

ngx_mail_init_connection

ngx_mail_init_session

ngx_mail_log_error

ngx_mail_read_command

ngx_mail_salt

ngx_mail_send

ngx_mail_session_internal_server_error

ngx_mail_ssl_handshake_handler

ngx_mail_ssl_init_connection

ngx_mail_starttls_handler

ngx_mail_starttls_only

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

static void ngx_mail_init_session(ngx_connection_t *c);

#if (NGX_MAIL_SSL)

static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);

static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);

#endif

void

‌ngx_mail_init_connection(ngx_connection_t *c)

{

    size_t                 len;

    ngx_uint_t             i;

    ngx_mail_port_t       *port;

    struct sockaddr       *sa;

    struct sockaddr_in    *sin;

    ngx_mail_log_ctx_t    *ctx;

    ngx_mail_in_addr_t    *addr;

    ngx_mail_session_t    *s;

    ngx_mail_addr_conf_t  *addr_conf;

    u_char                 text[NGX_SOCKADDR_STRLEN];

#if (NGX_HAVE_INET6)

    struct sockaddr_in6   *sin6;

    ngx_mail_in6_addr_t   *addr6;

#endif

    /* find the server configuration for the address:port */

    port = c->listening->servers;

    if (port->naddrs > 1) {

        /*

         * There are several addresses on this port and one of them

         * is the "*:port" wildcard so getsockname() is needed to determine

         * the server address.

         *

         * AcceptEx() already gave this address.

         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {

            ngx_mail_close_connection(c);

            return;

        }

        sa = c->local_sockaddr;

        switch (sa->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) sa;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {

                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {

                    break;

                }

            }

            addr_conf = &addr6[i].conf;

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) sa;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {

                if (addr[i].addr == sin->sin_addr.s_addr) {

                    break;

                }

            }

            addr_conf = &addr[i].conf;

            break;

        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            addr6 = port->addrs;

            addr_conf = &addr6[0].conf;

            break;

#endif

        default: /* AF_INET */

            addr = port->addrs;

            addr_conf = &addr[0].conf;

            break;

        }

    }

    s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));

    if (s == NULL) {

        ngx_mail_close_connection(c);

        return;

    }

    s->signature = NGX_MAIL_MODULE;

    s->main_conf = addr_conf->ctx->main_conf;

    s->srv_conf = addr_conf->ctx->srv_conf;

    s->addr_text = &addr_conf->addr_text;

    c->data = s;

    s->connection = c;

    len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);

    ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",

                  c->number, len, text, s->addr_text);

    ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));

    if (ctx == NULL) {

        ngx_mail_close_connection(c);

        return;

    }

    ctx->client = &c->addr_text;

    ctx->session = s;

    c->log->connection = c->number;

    c->log->handler = ngx_mail_log_error;

    c->log->data = ctx;

    c->log->action = "sending client greeting line";

    c->log_error = NGX_ERROR_INFO;

#if (NGX_MAIL_SSL)

    {

    ngx_mail_ssl_conf_t  *sslcf;

    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

    if (sslcf->enable) {

        c->log->action = "SSL handshaking";

        ngx_mail_ssl_init_connection(&sslcf->ssl, c);

        return;

    }

    if (addr_conf->ssl) {

        c->log->action = "SSL handshaking";

        if (sslcf->ssl.ctx == NULL) {

            ngx_log_error(NGX_LOG_ERR, c->log, 0,

                          "no \"ssl_certificate\" is defined "

                          "in server listening on SSL port");

            ngx_mail_close_connection(c);

            return;

        }

        ngx_mail_ssl_init_connection(&sslcf->ssl, c);

        return;

    }

    }

#endif

    ngx_mail_init_session(c);

}

#if (NGX_MAIL_SSL)

void

‌ngx_mail_starttls_handler(ngx_event_t *rev)

{

    ngx_connection_t     *c;

    ngx_mail_session_t   *s;

    ngx_mail_ssl_conf_t  *sslcf;

    c = rev->data;

    s = c->data;

    s->starttls = 1;

    c->log->action = "in starttls state";

    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

    ngx_mail_ssl_init_connection(&sslcf->ssl, c);

}

static void

‌ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)

{

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {

        ngx_mail_close_connection(c);

        return;

    }

    if (ngx_ssl_handshake(c) == NGX_AGAIN) {

        s = c->data;

        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

        ngx_add_timer(c->read, cscf->timeout);

        c->ssl->handler = ngx_mail_ssl_handshake_handler;

        return;

    }

    ngx_mail_ssl_handshake_handler(c);

}

static void

‌ngx_mail_ssl_handshake_handler(ngx_connection_t *c)

{

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    if (c->ssl->handshaked) {

        s = c->data;

        if (s->starttls) {

            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

            c->read->handler = cscf->protocol->init_protocol;

            c->write->handler = ngx_mail_send;

            cscf->protocol->init_protocol(c->read);

            return;

        }

        c->read->ready = 0;

        ngx_mail_init_session(c);

        return;

    }

    ngx_mail_close_connection(c);

}

#endif

static void

‌ngx_mail_init_session(ngx_connection_t *c)

{

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    s = c->data;

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    s->protocol = cscf->protocol->type;

    s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);

    if (s->ctx == NULL) {

        ngx_mail_session_internal_server_error(s);

        return;

    }

    c->write->handler = ngx_mail_send;

    cscf->protocol->init_session(s, c);

}

ngx_int_t

‌ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,

    ngx_mail_core_srv_conf_t *cscf)

{

    s->salt.data = ngx_pnalloc(c->pool,

                               sizeof(" <18446744073709551616.@>" CRLF) - 1

                               + NGX_TIME_T_LEN

                               + cscf->server_name.len);

    if (s->salt.data == NULL) {

        return NGX_ERROR;

    }

    s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,

                              ngx_random(), ngx_time(), &cscf->server_name)

                  - s->salt.data;

    return NGX_OK;

}

#if (NGX_MAIL_SSL)

ngx_int_t

‌ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_mail_ssl_conf_t  *sslcf;

    if (c->ssl) {

        return 0;

    }

    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {

        return 1;

    }

    return 0;

}

#endif

ngx_int_t

‌ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)

{

    u_char     *p, *last;

    ngx_str_t  *arg, plain;

    arg = s->args.elts;

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth plain: \"%V\"", &arg[n]);

#endif

    plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));

    if (plain.data == NULL) {

        return NGX_ERROR;

    }

    if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

            "client sent invalid base64 encoding in AUTH PLAIN command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    p = plain.data;

    last = p + plain.len;

    while (p < last && *p++) { /* void */ }

    if (p == last) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client sent invalid login in AUTH PLAIN command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    s->login.data = p;

    while (p < last && *p) { p++; }

    if (p == last) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client sent invalid password in AUTH PLAIN command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    s->login.len = p++ - s->login.data;

    s->passwd.len = last - p;

    s->passwd.data = p;

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);

#endif

    return NGX_DONE;

}

ngx_int_t

‌ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,

    ngx_uint_t n)

{

    ngx_str_t  *arg;

    arg = s->args.elts;

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth login username: \"%V\"", &arg[n]);

    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));

    if (s->login.data == NULL) {

        return NGX_ERROR;

    }

    if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

            "client sent invalid base64 encoding in AUTH LOGIN command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth login username: \"%V\"", &s->login);

    return NGX_OK;

}

ngx_int_t

‌ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t  *arg;

    arg = s->args.elts;

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth login password: \"%V\"", &arg[0]);

#endif

    s->passwd.data = ngx_pnalloc(c->pool,

                                 ngx_base64_decoded_length(arg[0].len));

    if (s->passwd.data == NULL) {

        return NGX_ERROR;

    }

    if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

            "client sent invalid base64 encoding in AUTH LOGIN command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth login password: \"%V\"", &s->passwd);

#endif

    return NGX_DONE;

}

ngx_int_t

‌ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,

    char *prefix, size_t len)

{

    u_char      *p;

    ngx_str_t    salt;

    ngx_uint_t   n;

    p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);

    if (p == NULL) {

        return NGX_ERROR;

    }

    salt.data = ngx_cpymem(p, prefix, len);

    s->salt.len -= 2;

    ngx_encode_base64(&salt, &s->salt);

    s->salt.len += 2;

    n = len + salt.len;

    p[n++] = CR; p[n++] = LF;

    s->out.len = n;

    s->out.data = p;

    return NGX_OK;

}

ngx_int_t

‌ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)

{

    u_char     *p, *last;

    ngx_str_t  *arg;

    arg = s->args.elts;

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth cram-md5: \"%V\"", &arg[0]);

    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));

    if (s->login.data == NULL) {

        return NGX_ERROR;

    }

    if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

            "client sent invalid base64 encoding in AUTH CRAM-MD5 command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    p = s->login.data;

    last = p + s->login.len;

    while (p < last) {

        if (*p++ == ' ') {

            s->login.len = p - s->login.data - 1;

            s->passwd.len = last - p;

            s->passwd.data = p;

            break;

        }

    }

    if (s->passwd.len != 32) {

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

            "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);

    s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;

    return NGX_DONE;

}

void

‌ngx_mail_send(ngx_event_t *wev)

{

    ngx_int_t                  n;

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    c = wev->data;

    s = c->data;

    if (wev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    if (s->out.len == 0) {

        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

            ngx_mail_close_connection(c);

        }

        return;

    }

    n = c->send(c, s->out.data, s->out.len);

    if (n > 0) {

        s->out.data += n;

        s->out.len -= n;

        if (s->out.len != 0) {

            goto again;

        }

        if (wev->timer_set) {

            ngx_del_timer(wev);

        }

        if (s->quit) {

            ngx_mail_close_connection(c);

            return;

        }

        if (s->blocked) {

            c->read->handler(c->read);

        }

        return;

    }

    if (n == NGX_ERROR) {

        ngx_mail_close_connection(c);

        return;

    }

    /* n == NGX_AGAIN */

again:

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    ngx_add_timer(c->write, cscf->timeout);

    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

        ngx_mail_close_connection(c);

        return;

    }

}

ngx_int_t

‌ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ssize_t                    n;

    ngx_int_t                  rc;

    ngx_str_t                  l;

    ngx_mail_core_srv_conf_t  *cscf;

    n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);

    if (n == NGX_ERROR || n == 0) {

        ngx_mail_close_connection(c);

        return NGX_ERROR;

    }

    if (n > 0) {

        s->buffer->last += n;

    }

    if (n == NGX_AGAIN) {

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            ngx_mail_session_internal_server_error(s);

            return NGX_ERROR;

        }

        if (s->buffer->pos == s->buffer->last) {

            return NGX_AGAIN;

        }

    }

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    rc = cscf->protocol->parse_command(s);

    if (rc == NGX_AGAIN) {

        if (s->buffer->last < s->buffer->end) {

            return rc;

        }

        l.len = s->buffer->last - s->buffer->start;

        l.data = s->buffer->start;

        ngx_log_error(NGX_LOG_INFO, c->log, 0,

                      "client sent too long command \"%V\"", &l);

        s->quit = 1;

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {

        return rc;

    }

    if (rc == NGX_ERROR) {

        ngx_mail_close_connection(c);

        return NGX_ERROR;

    }

    return NGX_OK;

}

void

‌ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)

{

    s->args.nelts = 0;

    if (s->buffer->pos == s->buffer->last) {

        s->buffer->pos = s->buffer->start;

        s->buffer->last = s->buffer->start;

    }

    s->state = 0;

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    s->login_attempt++;

    ngx_mail_auth_http_init(s);

}

void

‌ngx_mail_session_internal_server_error(ngx_mail_session_t *s)

{

    ngx_mail_core_srv_conf_t  *cscf;

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    s->out = cscf->protocol->internal_server_error;

    s->quit = 1;

    ngx_mail_send(s->connection->write);

}

void

‌ngx_mail_close_connection(ngx_connection_t *c)

{

    ngx_pool_t  *pool;

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "close mail connection: %d", c->fd);

#if (NGX_MAIL_SSL)

    if (c->ssl) {

        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {

            c->ssl->handler = ngx_mail_close_connection;

            return;

        }

    }

#endif

#if (NGX_STAT_STUB)

    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);

#endif

    c->destroyed = 1;

    pool = c->pool;

    ngx_close_connection(c);

    ngx_destroy_pool(pool);

}

u_char *

‌ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)

{

    u_char              *p;

    ngx_mail_session_t  *s;

    ngx_mail_log_ctx_t  *ctx;

    if (log->action) {

        p = ngx_snprintf(buf, len, " while %s", log->action);

        len -= p - buf;

        buf = p;

    }

    ctx = log->data;

    p = ngx_snprintf(buf, len, ", client: %V", ctx->client);

    len -= p - buf;

    buf = p;

    s = ctx->session;

    if (s == NULL) {

        return p;

    }

    p = ngx_snprintf(buf, len, "%s, server: %V",

                     s->starttls ? " using starttls" : "",

                     s->addr_text);

    len -= p - buf;

    buf = p;

    if (s->login.len == 0) {

        return p;

    }

    p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);

    len -= p - buf;

    buf = p;

    if (s->proxy == NULL) {

        return p;

    }

    p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);

    return p;

}

src/mail/ngx_mail_core_module.c - nginx-1.7.9

Global variables defined

ngx_conf_deprecated_so_keepalive

ngx_mail_core_commands

ngx_mail_core_module

ngx_mail_core_module_ctx

Functions defined

ngx_mail_capabilities

ngx_mail_core_create_main_conf

ngx_mail_core_create_srv_conf

ngx_mail_core_listen

ngx_mail_core_merge_srv_conf

ngx_mail_core_protocol

ngx_mail_core_resolver

ngx_mail_core_server

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

static void *ngx_mail_core_create_main_conf(ngx_conf_t *cf);

static void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf);

static char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_conf_deprecated_t  ngx_conf_deprecated_so_keepalive = {

    ngx_conf_deprecated, "so_keepalive",

    "so_keepalive\" parameter of the \"listen"

};

‌static ngx_command_t  ngx_mail_core_commands[] = {

    { ngx_string("server"),

      NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_mail_core_server,

      0,

      0,

      NULL },

    { ngx_string("listen"),

      NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_mail_core_listen,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("protocol"),

      NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_mail_core_protocol,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("so_keepalive"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_core_srv_conf_t, so_keepalive),

      &ngx_conf_deprecated_so_keepalive },

    { ngx_string("timeout"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_core_srv_conf_t, timeout),

      NULL },

    { ngx_string("server_name"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_core_srv_conf_t, server_name),

      NULL },

    { ngx_string("resolver"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_mail_core_resolver,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("resolver_timeout"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),

      NULL },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_core_module_ctx = {

    NULL,                                  /* protocol */

    ngx_mail_core_create_main_conf,        /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_core_create_srv_conf,         /* create server configuration */

    ngx_mail_core_merge_srv_conf           /* merge server configuration */

};

‌ngx_module_t  ngx_mail_core_module = {

    NGX_MODULE_V1,

    &ngx_mail_core_module_ctx,             /* module context */

    ngx_mail_core_commands,                /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static void *

‌ngx_mail_core_create_main_conf(ngx_conf_t *cf)

{

    ngx_mail_core_main_conf_t  *cmcf;

    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));

    if (cmcf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&cmcf->servers, cf->pool, 4,

                       sizeof(ngx_mail_core_srv_conf_t *))

        != NGX_OK)

    {

        return NULL;

    }

    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))

        != NGX_OK)

    {

        return NULL;

    }

    return cmcf;

}

static void *

‌ngx_mail_core_create_srv_conf(ngx_conf_t *cf)

{

    ngx_mail_core_srv_conf_t  *cscf;

    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t));

    if (cscf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     cscf->protocol = NULL;

     */

    cscf->timeout = NGX_CONF_UNSET_MSEC;

    cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;

    cscf->so_keepalive = NGX_CONF_UNSET;

    cscf->resolver = NGX_CONF_UNSET_PTR;

    cscf->file_name = cf->conf_file->file.name.data;

    cscf->line = cf->conf_file->line;

    return cscf;

}

static char *

‌ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_core_srv_conf_t *prev = parent;

    ngx_mail_core_srv_conf_t *conf = child;

    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);

    ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,

                              30000);

    ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);

    ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");

    if (conf->server_name.len == 0) {

        conf->server_name = cf->cycle->hostname;

    }

    if (conf->protocol == NULL) {

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "unknown mail protocol for server in %s:%ui",

                      conf->file_name, conf->line);

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                       *rv;

    void                       *mconf;

    ngx_uint_t                  m;

    ngx_conf_t                  pcf;

    ngx_mail_module_t          *module;

    ngx_mail_conf_ctx_t        *ctx, *mail_ctx;

    ngx_mail_core_srv_conf_t   *cscf, **cscfp;

    ngx_mail_core_main_conf_t  *cmcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    mail_ctx = cf->ctx;

    ctx->main_conf = mail_ctx->main_conf;

    /* the server{}'s srv_conf */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);

    if (ctx->srv_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->create_srv_conf) {

            mconf = module->create_srv_conf(cf);

            if (mconf == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;

        }

    }

    /* the server configuration context */

    cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];

    cscf->ctx = ctx;

    cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];

    cscfp = ngx_array_push(&cmcf->servers);

    if (cscfp == NULL) {

        return NGX_CONF_ERROR;

    }

    *cscfp = cscf;

    /* parse inside server{} */

    pcf = *cf;

    cf->ctx = ctx;

    cf->cmd_type = NGX_MAIL_SRV_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    return rv;

}

static char *

‌ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_core_srv_conf_t  *cscf = conf;

    size_t                      len, off;

    in_port_t                   port;

    ngx_str_t                  *value;

    ngx_url_t                   u;

    ngx_uint_t                  i, m;

    struct sockaddr            *sa;

    ngx_mail_listen_t          *ls;

    ngx_mail_module_t          *module;

    struct sockaddr_in         *sin;

    ngx_mail_core_main_conf_t  *cmcf;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6        *sin6;

#endif

    value = cf->args->elts;

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.listen = 1;

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "%s in \"%V\" of the \"listen\" directive",

                               u.err, &u.url);

        }

        return NGX_CONF_ERROR;

    }

    cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);

    ls = cmcf->listen.elts;

    for (i = 0; i < cmcf->listen.nelts; i++) {

        sa = (struct sockaddr *) ls[i].sockaddr;

        if (sa->sa_family != u.family) {

            continue;

        }

        switch (sa->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            off = offsetof(struct sockaddr_in6, sin6_addr);

            len = 16;

            sin6 = (struct sockaddr_in6 *) sa;

            port = sin6->sin6_port;

            break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        case AF_UNIX:

            off = offsetof(struct sockaddr_un, sun_path);

            len = sizeof(((struct sockaddr_un *) sa)->sun_path);

            port = 0;

            break;

#endif

        default: /* AF_INET */

            off = offsetof(struct sockaddr_in, sin_addr);

            len = 4;

            sin = (struct sockaddr_in *) sa;

            port = sin->sin_port;

            break;

        }

        if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {

            continue;

        }

        if (port != u.port) {

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "duplicate \"%V\" address and port pair", &u.url);

        return NGX_CONF_ERROR;

    }

    ls = ngx_array_push(&cmcf->listen);

    if (ls == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(ls, sizeof(ngx_mail_listen_t));

    ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);

    ls->socklen = u.socklen;

    ls->wildcard = u.wildcard;

    ls->ctx = cf->ctx;

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    ls->ipv6only = 1;

#endif

    if (cscf->protocol == NULL) {

        for (m = 0; ngx_modules[m]; m++) {

            if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

                continue;

            }

            module = ngx_modules[m]->ctx;

            if (module->protocol == NULL) {

                continue;

            }

            for (i = 0; module->protocol->port[i]; i++) {

                if (module->protocol->port[i] == u.port) {

                    cscf->protocol = module->protocol;

                    break;

                }

            }

        }

    }

    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "bind") == 0) {

            ls->bind = 1;

            continue;

        }

        if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

            struct sockaddr  *sa;

            u_char            buf[NGX_SOCKADDR_STRLEN];

            sa = (struct sockaddr *) ls->sockaddr;

            if (sa->sa_family == AF_INET6) {

                if (ngx_strcmp(&value[i].data[10], "n") == 0) {

                    ls->ipv6only = 1;

                } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {

                    ls->ipv6only = 0;

                } else {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "invalid ipv6only flags \"%s\"",

                                       &value[i].data[9]);

                    return NGX_CONF_ERROR;

                }

                ls->bind = 1;

            } else {

                len = ngx_sock_ntop(sa, ls->socklen, buf,

                                    NGX_SOCKADDR_STRLEN, 1);

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "ipv6only is not supported "

                                   "on addr \"%*s\", ignored", len, buf);

            }

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "bind ipv6only is not supported "

                               "on this platform");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strcmp(value[i].data, "ssl") == 0) {

#if (NGX_MAIL_SSL)

            ls->ssl = 1;

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "the \"ssl\" parameter requires "

                               "ngx_mail_ssl_module");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {

            if (ngx_strcmp(&value[i].data[13], "on") == 0) {

                ls->so_keepalive = 1;

            } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {

                ls->so_keepalive = 2;

            } else {

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

                u_char     *p, *end;

                ngx_str_t   s;

                end = value[i].data + value[i].len;

                s.data = value[i].data + 13;

                p = ngx_strlchr(s.data, end, ':');

                if (p == NULL) {

                    p = end;

                }

                if (p > s.data) {

                    s.len = p - s.data;

                    ls->tcp_keepidle = ngx_parse_time(&s, 1);

                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                s.data = (p < end) ? (p + 1) : end;

                p = ngx_strlchr(s.data, end, ':');

                if (p == NULL) {

                    p = end;

                }

                if (p > s.data) {

                    s.len = p - s.data;

                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);

                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                s.data = (p < end) ? (p + 1) : end;

                if (s.data < end) {

                    s.len = end - s.data;

                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);

                    if (ls->tcp_keepcnt == NGX_ERROR) {

                        goto invalid_so_keepalive;

                    }

                }

                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0

                    && ls->tcp_keepcnt == 0)

                {

                    goto invalid_so_keepalive;

                }

                ls->so_keepalive = 1;

#else

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "the \"so_keepalive\" parameter accepts "

                                   "only \"on\" or \"off\" on this platform");

                return NGX_CONF_ERROR;

#endif

            }

            ls->bind = 1;

            continue;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

        invalid_so_keepalive:

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid so_keepalive value: \"%s\"",

                               &value[i].data[13]);

            return NGX_CONF_ERROR;

#endif

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "the invalid \"%V\" parameter", &value[i]);

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_core_srv_conf_t  *cscf = conf;

    ngx_str_t          *value;

    ngx_uint_t          m;

    ngx_mail_module_t  *module;

    value = cf->args->elts;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->protocol

            && ngx_strcmp(module->protocol->name.data, value[1].data) == 0)

        {

            cscf->protocol = module->protocol;

            return NGX_CONF_OK;

        }

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "unknown protocol \"%V\"", &value[1]);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_core_srv_conf_t  *cscf = conf;

    ngx_str_t  *value;

    value = cf->args->elts;

    if (cscf->resolver != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        cscf->resolver = NULL;

        return NGX_CONF_OK;

    }

    cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);

    if (cscf->resolver == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

char *

‌ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t    *c, *value;

    ngx_uint_t    i;

    ngx_array_t  *a;

    a = (ngx_array_t *) (p + cmd->offset);

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        c = ngx_array_push(a);

        if (c == NULL) {

            return NGX_CONF_ERROR;

        }

        *c = value[i];

    }

    return NGX_CONF_OK;

}

src/mail/ngx_mail.c - nginx-1.7.9

Global variables defined

ngx_mail_commands

ngx_mail_max_module

ngx_mail_module

ngx_mail_module_ctx

Functions defined

ngx_mail_add_addrs

ngx_mail_add_addrs6

ngx_mail_add_ports

ngx_mail_block

ngx_mail_cmp_conf_addrs

ngx_mail_optimize_servers

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,

    ngx_mail_listen_t *listen);

static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);

static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,

    ngx_mail_conf_addr_t *addr);

#if (NGX_HAVE_INET6)

static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,

    ngx_mail_conf_addr_t *addr);

#endif

static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two);

‌ngx_uint_t  ngx_mail_max_module;

‌static ngx_command_t  ngx_mail_commands[] = {

    { ngx_string("mail"),

      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_mail_block,

      0,

      0,

      NULL },

    { ngx_string("imap"),

      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,

      ngx_mail_block,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_core_module_t  ngx_mail_module_ctx = {

    ngx_string("mail"),

    NULL,

    NULL

};

‌ngx_module_t  ngx_mail_module = {

    NGX_MODULE_V1,

    &ngx_mail_module_ctx,                  /* module context */

    ngx_mail_commands,                     /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static char *

‌ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                        *rv;

    ngx_uint_t                   i, m, mi, s;

    ngx_conf_t                   pcf;

    ngx_array_t                  ports;

    ngx_mail_listen_t           *listen;

    ngx_mail_module_t           *module;

    ngx_mail_conf_ctx_t         *ctx;

    ngx_mail_core_srv_conf_t   **cscfp;

    ngx_mail_core_main_conf_t   *cmcf;

    if (cmd->name.data[0] == 'i') {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "the \"imap\" directive is deprecated, "

                           "use the \"mail\" directive instead");

    }

    /* the main mail context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *(ngx_mail_conf_ctx_t **) conf = ctx;

    /* count the number of the http modules and set up their indices */

    ngx_mail_max_module = 0;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

            continue;

        }

        ngx_modules[m]->ctx_index = ngx_mail_max_module++;

    }

    /* the mail main_conf context, it is the same in the all mail contexts */

    ctx->main_conf = ngx_pcalloc(cf->pool,

                                 sizeof(void *) * ngx_mail_max_module);

    if (ctx->main_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /*

     * the mail null srv_conf context, it is used to merge

     * the server{}s' srv_conf's

     */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);

    if (ctx->srv_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    /*

     * create the main_conf's and the null srv_conf's of the all mail modules

     */

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        mi = ngx_modules[m]->ctx_index;

        if (module->create_main_conf) {

            ctx->main_conf[mi] = module->create_main_conf(cf);

            if (ctx->main_conf[mi] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        if (module->create_srv_conf) {

            ctx->srv_conf[mi] = module->create_srv_conf(cf);

            if (ctx->srv_conf[mi] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

    }

    /* parse inside the mail{} block */

    pcf = *cf;

    cf->ctx = ctx;

    cf->module_type = NGX_MAIL_MODULE;

    cf->cmd_type = NGX_MAIL_MAIN_CONF;

    rv = ngx_conf_parse(cf, NULL);

    if (rv != NGX_CONF_OK) {

        *cf = pcf;

        return rv;

    }

    /* init mail{} main_conf's, merge the server{}s' srv_conf's */

    cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];

    cscfp = cmcf->servers.elts;

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_MAIL_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        mi = ngx_modules[m]->ctx_index;

        /* init mail{} main_conf's */

        cf->ctx = ctx;

        if (module->init_main_conf) {

            rv = module->init_main_conf(cf, ctx->main_conf[mi]);

            if (rv != NGX_CONF_OK) {

                *cf = pcf;

                return rv;

            }

        }

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

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

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

            if (module->merge_srv_conf) {

                rv = module->merge_srv_conf(cf,

                                            ctx->srv_conf[mi],

                                            cscfp[s]->ctx->srv_conf[mi]);

                if (rv != NGX_CONF_OK) {

                    *cf = pcf;

                    return rv;

                }

            }

        }

    }

    *cf = pcf;

    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t))

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    listen = cmcf->listen.elts;

    for (i = 0; i < cmcf->listen.nelts; i++) {

        if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return ngx_mail_optimize_servers(cf, &ports);

}

static ngx_int_t

‌ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,

    ngx_mail_listen_t *listen)

{

    in_port_t              p;

    ngx_uint_t             i;

    struct sockaddr       *sa;

    struct sockaddr_in    *sin;

    ngx_mail_conf_port_t  *port;

    ngx_mail_conf_addr_t  *addr;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6   *sin6;

#endif

    sa = (struct sockaddr *) &listen->sockaddr;

    switch (sa->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) sa;

        p = sin6->sin6_port;

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        p = 0;

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) sa;

        p = sin->sin_port;

        break;

    }

    port = ports->elts;

    for (i = 0; i < ports->nelts; i++) {

        if (p == port[i].port && sa->sa_family == port[i].family) {

            /* a port is already in the port list */

            port = &port[i];

            goto found;

        }

    }

    /* add a port to the port list */

    port = ngx_array_push(ports);

    if (port == NULL) {

        return NGX_ERROR;

    }

    port->family = sa->sa_family;

    port->port = p;

    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,

                       sizeof(ngx_mail_conf_addr_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

found:

    addr = ngx_array_push(&port->addrs);

    if (addr == NULL) {

        return NGX_ERROR;

    }

    addr->sockaddr = (struct sockaddr *) &listen->sockaddr;

    addr->socklen = listen->socklen;

    addr->ctx = listen->ctx;

    addr->bind = listen->bind;

    addr->wildcard = listen->wildcard;

    addr->so_keepalive = listen->so_keepalive;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

    addr->tcp_keepidle = listen->tcp_keepidle;

    addr->tcp_keepintvl = listen->tcp_keepintvl;

    addr->tcp_keepcnt = listen->tcp_keepcnt;

#endif

#if (NGX_MAIL_SSL)

    addr->ssl = listen->ssl;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    addr->ipv6only = listen->ipv6only;

#endif

    return NGX_OK;

}

static char *

‌ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)

{

    ngx_uint_t             i, p, last, bind_wildcard;

    ngx_listening_t       *ls;

    ngx_mail_port_t       *mport;

    ngx_mail_conf_port_t  *port;

    ngx_mail_conf_addr_t  *addr;

    port = ports->elts;

    for (p = 0; p < ports->nelts; p++) {

        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,

                 sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs);

        addr = port[p].addrs.elts;

        last = port[p].addrs.nelts;

        /*

         * if there is the binding to the "*:port" then we need to bind()

         * to the "*:port" only and ignore the other bindings

         */

        if (addr[last - 1].wildcard) {

            addr[last - 1].bind = 1;

            bind_wildcard = 1;

        } else {

            bind_wildcard = 0;

        }

        i = 0;

        while (i < last) {

            if (bind_wildcard && !addr[i].bind) {

                i++;

                continue;

            }

            ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);

            if (ls == NULL) {

                return NGX_CONF_ERROR;

            }

            ls->addr_ntop = 1;

            ls->handler = ngx_mail_init_connection;

            ls->pool_size = 256;

            /* TODO: error_log directive */

            ls->logp = &cf->cycle->new_log;

            ls->log.data = &ls->addr_text;

            ls->log.handler = ngx_accept_log_error;

            ls->keepalive = addr[i].so_keepalive;

#if (NGX_HAVE_KEEPALIVE_TUNABLE)

            ls->keepidle = addr[i].tcp_keepidle;

            ls->keepintvl = addr[i].tcp_keepintvl;

            ls->keepcnt = addr[i].tcp_keepcnt;

#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

            ls->ipv6only = addr[i].ipv6only;

#endif

            mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t));

            if (mport == NULL) {

                return NGX_CONF_ERROR;

            }

            ls->servers = mport;

            if (i == last - 1) {

                mport->naddrs = last;

            } else {

                mport->naddrs = 1;

                i = 0;

            }

            switch (ls->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

            case AF_INET6:

                if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) {

                    return NGX_CONF_ERROR;

                }

                break;

#endif

            default: /* AF_INET */

                if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) {

                    return NGX_CONF_ERROR;

                }

                break;

            }

            addr++;

            last--;

        }

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,

    ngx_mail_conf_addr_t *addr)

{

    u_char              *p;

    size_t               len;

    ngx_uint_t           i;

    ngx_mail_in_addr_t  *addrs;

    struct sockaddr_in  *sin;

    u_char               buf[NGX_SOCKADDR_STRLEN];

    mport->addrs = ngx_pcalloc(cf->pool,

                               mport->naddrs * sizeof(ngx_mail_in_addr_t));

    if (mport->addrs == NULL) {

        return NGX_ERROR;

    }

    addrs = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].sockaddr;

        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].ctx;

#if (NGX_MAIL_SSL)

        addrs[i].conf.ssl = addr[i].ssl;

#endif

        len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,

                            NGX_SOCKADDR_STRLEN, 1);

        p = ngx_pnalloc(cf->pool, len);

        if (p == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(p, buf, len);

        addrs[i].conf.addr_text.len = len;

        addrs[i].conf.addr_text.data = p;

    }

    return NGX_OK;

}

#if (NGX_HAVE_INET6)

static ngx_int_t

‌ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,

    ngx_mail_conf_addr_t *addr)

{

    u_char               *p;

    size_t                len;

    ngx_uint_t            i;

    ngx_mail_in6_addr_t  *addrs6;

    struct sockaddr_in6  *sin6;

    u_char                buf[NGX_SOCKADDR_STRLEN];

    mport->addrs = ngx_pcalloc(cf->pool,

                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));

    if (mport->addrs == NULL) {

        return NGX_ERROR;

    }

    addrs6 = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;

        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].ctx;

#if (NGX_MAIL_SSL)

        addrs6[i].conf.ssl = addr[i].ssl;

#endif

        len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,

                            NGX_SOCKADDR_STRLEN, 1);

        p = ngx_pnalloc(cf->pool, len);

        if (p == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(p, buf, len);

        addrs6[i].conf.addr_text.len = len;

        addrs6[i].conf.addr_text.data = p;

    }

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_mail_cmp_conf_addrs(const void *one, const void *two)

{

    ngx_mail_conf_addr_t  *first, *second;

    first = (ngx_mail_conf_addr_t *) one;

    second = (ngx_mail_conf_addr_t *) two;

    if (first->wildcard) {

        /* a wildcard must be the last resort, shift it to the end */

        return 1;

    }

    if (second->wildcard) {

        /* a wildcard must be the last resort, shift it to the end */

        return -1;

    }

    if (first->bind && !second->bind) {

        /* shift explicit bind()ed addresses to the start */

        return -1;

    }

    if (!first->bind && second->bind) {

        /* shift explicit bind()ed addresses to the start */

        return 1;

    }

    /* do not sort by default */

    return 0;

}

src/event/ngx_event_accept.c - nginx-1.7.9

Functions defined

ngx_accept_log_error

ngx_close_accepted_connection

ngx_disable_accept_events

ngx_enable_accept_events

ngx_event_accept

ngx_trylock_accept_mutex

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);

static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);

static void ngx_close_accepted_connection(ngx_connection_t *c);

void

‌ngx_event_accept(ngx_event_t *ev)

{

    socklen_t          socklen;

    ngx_err_t          err;

    ngx_log_t         *log;

    ngx_uint_t         level;

    ngx_socket_t       s;

    ngx_event_t       *rev, *wev;

    ngx_listening_t   *ls;

    ngx_connection_t  *c, *lc;

    ngx_event_conf_t  *ecf;

    u_char             sa[NGX_SOCKADDRLEN];

#if (NGX_HAVE_ACCEPT4)

    static ngx_uint_t  use_accept4 = 1;

#endif

    if (ev->timedout) {

        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {

            return;

        }

        ev->timedout = 0;

    }

    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);

    if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

        ev->available = 1;

    } else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {

        ev->available = ecf->multi_accept;

    }

    lc = ev->data;

    ls = lc->listening;

    ev->ready = 0;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "accept on %V, ready: %d", &ls->addr_text, ev->available);

    do {

        socklen = NGX_SOCKADDRLEN;

#if (NGX_HAVE_ACCEPT4)

        if (use_accept4) {

            s = accept4(lc->fd, (struct sockaddr *) sa, &socklen,

                        SOCK_NONBLOCK);

        } else {

            s = accept(lc->fd, (struct sockaddr *) sa, &socklen);

        }

#else

        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);

#endif

        if (s == (ngx_socket_t) -1) {

            err = ngx_socket_errno;

            if (err == NGX_EAGAIN) {

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,

                               "accept() not ready");

                return;

            }

            level = NGX_LOG_ALERT;

            if (err == NGX_ECONNABORTED) {

                level = NGX_LOG_ERR;

            } else if (err == NGX_EMFILE || err == NGX_ENFILE) {

                level = NGX_LOG_CRIT;

            }

#if (NGX_HAVE_ACCEPT4)

            ngx_log_error(level, ev->log, err,

                          use_accept4 ? "accept4() failed" : "accept() failed");

            if (use_accept4 && err == NGX_ENOSYS) {

                use_accept4 = 0;

                ngx_inherited_nonblocking = 0;

                continue;

            }

#else

            ngx_log_error(level, ev->log, err, "accept() failed");

#endif

            if (err == NGX_ECONNABORTED) {

                if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

                    ev->available--;

                }

                if (ev->available) {

                    continue;

                }

            }

            if (err == NGX_EMFILE || err == NGX_ENFILE) {

                if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle)

                    != NGX_OK)

                {

                    return;

                }

                if (ngx_use_accept_mutex) {

                    if (ngx_accept_mutex_held) {

                        ngx_shmtx_unlock(&ngx_accept_mutex);

                        ngx_accept_mutex_held = 0;

                    }

                    ngx_accept_disabled = 1;

                } else {

                    ngx_add_timer(ev, ecf->accept_mutex_delay);

                }

            }

            return;

        }

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);

#endif

        ngx_accept_disabled = ngx_cycle->connection_n / 8

                              - ngx_cycle->free_connection_n;

        c = ngx_get_connection(s, ev->log);

        if (c == NULL) {

            if (ngx_close_socket(s) == -1) {

                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,

                              ngx_close_socket_n " failed");

            }

            return;

        }

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);

#endif

        c->pool = ngx_create_pool(ls->pool_size, ev->log);

        if (c->pool == NULL) {

            ngx_close_accepted_connection(c);

            return;

        }

        c->sockaddr = ngx_palloc(c->pool, socklen);

        if (c->sockaddr == NULL) {

            ngx_close_accepted_connection(c);

            return;

        }

        ngx_memcpy(c->sockaddr, sa, socklen);

        log = ngx_palloc(c->pool, sizeof(ngx_log_t));

        if (log == NULL) {

            ngx_close_accepted_connection(c);

            return;

        }

        /* set a blocking mode for aio and non-blocking mode for others */

        if (ngx_inherited_nonblocking) {

            if (ngx_event_flags & NGX_USE_AIO_EVENT) {

                if (ngx_blocking(s) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,

                                  ngx_blocking_n " failed");

                    ngx_close_accepted_connection(c);

                    return;

                }

            }

        } else {

            if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) {

                if (ngx_nonblocking(s) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,

                                  ngx_nonblocking_n " failed");

                    ngx_close_accepted_connection(c);

                    return;

                }

            }

        }

        *log = ls->log;

        c->recv = ngx_recv;

        c->send = ngx_send;

        c->recv_chain = ngx_recv_chain;

        c->send_chain = ngx_send_chain;

        c->log = log;

        c->pool->log = log;

        c->socklen = socklen;

        c->listening = ls;

        c->local_sockaddr = ls->sockaddr;

        c->local_socklen = ls->socklen;

        c->unexpected_eof = 1;

#if (NGX_HAVE_UNIX_DOMAIN)

        if (c->sockaddr->sa_family == AF_UNIX) {

            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;

            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;

#if (NGX_SOLARIS)

            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */

            c->sendfile = 0;

#endif

        }

#endif

        rev = c->read;

        wev = c->write;

        wev->ready = 1;

        if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {

            /* rtsig, aio, iocp */

            rev->ready = 1;

        }

        if (ev->deferred_accept) {

            rev->ready = 1;

#if (NGX_HAVE_KQUEUE)

            rev->available = 1;

#endif

        }

        rev->log = log;

        wev->log = log;

        /*

         * TODO: MT: - ngx_atomic_fetch_add()

         *             or protection by critical section or light mutex

         *

         * TODO: MP: - allocated in a shared memory

         *           - ngx_atomic_fetch_add()

         *             or protection by critical section or light mutex

         */

        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);

#endif

        if (ls->addr_ntop) {

            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);

            if (c->addr_text.data == NULL) {

                ngx_close_accepted_connection(c);

                return;

            }

            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,

                                             c->addr_text.data,

                                             ls->addr_text_max_len, 0);

            if (c->addr_text.len == 0) {

                ngx_close_accepted_connection(c);

                return;

            }

        }

#if (NGX_DEBUG)

        {

        ngx_str_t             addr;

        struct sockaddr_in   *sin;

        ngx_cidr_t           *cidr;

        ngx_uint_t            i;

        u_char                text[NGX_SOCKADDR_STRLEN];

#if (NGX_HAVE_INET6)

        struct sockaddr_in6  *sin6;

        ngx_uint_t            n;

#endif

        cidr = ecf->debug_connection.elts;

        for (i = 0; i < ecf->debug_connection.nelts; i++) {

            if (cidr[i].family != (ngx_uint_t) c->sockaddr->sa_family) {

                goto next;

            }

            switch (cidr[i].family) {

#if (NGX_HAVE_INET6)

            case AF_INET6:

                sin6 = (struct sockaddr_in6 *) c->sockaddr;

                for (n = 0; n < 16; n++) {

                    if ((sin6->sin6_addr.s6_addr[n]

                        & cidr[i].u.in6.mask.s6_addr[n])

                        != cidr[i].u.in6.addr.s6_addr[n])

                    {

                        goto next;

                    }

                }

                break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

            case AF_UNIX:

                break;

#endif

            default: /* AF_INET */

                sin = (struct sockaddr_in *) c->sockaddr;

                if ((sin->sin_addr.s_addr & cidr[i].u.in.mask)

                    != cidr[i].u.in.addr)

                {

                    goto next;

                }

                break;

            }

            log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL;

            break;

        next:

            continue;

        }

        if (log->log_level & NGX_LOG_DEBUG_EVENT) {

            addr.data = text;

            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,

                                     NGX_SOCKADDR_STRLEN, 1);

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,

                           "*%uA accept: %V fd:%d", c->number, &addr, s);

        }

        }

#endif

        if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {

            if (ngx_add_conn(c) == NGX_ERROR) {

                ngx_close_accepted_connection(c);

                return;

            }

        }

        log->data = NULL;

        log->handler = NULL;

        ls->handler(c);

        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

            ev->available--;

        }

    } while (ev->available);

}

ngx_int_t

‌ngx_trylock_accept_mutex(ngx_cycle_t *cycle)

{

    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "accept mutex locked");

        if (ngx_accept_mutex_held

            && ngx_accept_events == 0

            && !(ngx_event_flags & NGX_USE_RTSIG_EVENT))

        {

            return NGX_OK;

        }

        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {

            ngx_shmtx_unlock(&ngx_accept_mutex);

            return NGX_ERROR;

        }

        ngx_accept_events = 0;

        ngx_accept_mutex_held = 1;

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "accept mutex lock failed: %ui", ngx_accept_mutex_held);

    if (ngx_accept_mutex_held) {

        if (ngx_disable_accept_events(cycle) == NGX_ERROR) {

            return NGX_ERROR;

        }

        ngx_accept_mutex_held = 0;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_enable_accept_events(ngx_cycle_t *cycle)

{

    ngx_uint_t         i;

    ngx_listening_t   *ls;

    ngx_connection_t  *c;

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        c = ls[i].connection;

        if (c->read->active) {

            continue;

        }

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

            if (ngx_add_conn(c) == NGX_ERROR) {

                return NGX_ERROR;

            }

        } else {

            if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_disable_accept_events(ngx_cycle_t *cycle)

{

    ngx_uint_t         i;

    ngx_listening_t   *ls;

    ngx_connection_t  *c;

    ls = cycle->listening.elts;

    for (i = 0; i < cycle->listening.nelts; i++) {

        c = ls[i].connection;

        if (!c->read->active) {

            continue;

        }

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {

            if (ngx_del_conn(c, NGX_DISABLE_EVENT) == NGX_ERROR) {

                return NGX_ERROR;

            }

        } else {

            if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)

                == NGX_ERROR)

            {

                return NGX_ERROR;

            }

        }

    }

    return NGX_OK;

}

static void

‌ngx_close_accepted_connection(ngx_connection_t *c)

{

    ngx_socket_t  fd;

    ngx_free_connection(c);

    fd = c->fd;

    c->fd = (ngx_socket_t) -1;

    if (ngx_close_socket(fd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,

                      ngx_close_socket_n " failed");

    }

    if (c->pool) {

        ngx_destroy_pool(c->pool);

    }

#if (NGX_STAT_STUB)

    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);

#endif

}

u_char *

‌ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len)

{

    return ngx_snprintf(buf, len, " while accepting new connection on %V",

                        log->data);

}

src/mail/ngx_mail_auth_http_module.c - nginx-1.7.9

Global variables defined

ngx_mail_auth_http_commands

ngx_mail_auth_http_method

ngx_mail_auth_http_module

ngx_mail_auth_http_module_ctx

ngx_mail_smtp_errcode

Data types defined

ngx_mail_auth_http_conf_t

ngx_mail_auth_http_ctx_s

ngx_mail_auth_http_ctx_t

ngx_mail_auth_http_handler_pt

Functions defined

ngx_mail_auth_http

ngx_mail_auth_http_block_read

ngx_mail_auth_http_create_conf

ngx_mail_auth_http_create_request

ngx_mail_auth_http_dummy_handler

ngx_mail_auth_http_escape

ngx_mail_auth_http_header

ngx_mail_auth_http_ignore_status_line

ngx_mail_auth_http_init

ngx_mail_auth_http_merge_conf

ngx_mail_auth_http_parse_header_line

ngx_mail_auth_http_process_headers

ngx_mail_auth_http_read_handler

ngx_mail_auth_http_write_handler

ngx_mail_auth_sleep_handler

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

#include <ngx_mail.h>

typedef struct {

    ngx_addr_t                     *peer;

    ngx_msec_t                      timeout;

    ngx_str_t                       host_header;

    ngx_str_t                       uri;

    ngx_str_t                       header;

    ngx_array_t                    *headers;

    u_char                         *file;

    ngx_uint_t                      line;

‌} ngx_mail_auth_http_conf_t;

‌typedef struct ngx_mail_auth_http_ctx_s  ngx_mail_auth_http_ctx_t;

‌typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx);

‌struct ngx_mail_auth_http_ctx_s {

    ngx_buf_t                      *request;

    ngx_buf_t                      *response;

    ngx_peer_connection_t           peer;

    ngx_mail_auth_http_handler_pt   handler;

    ngx_uint_t                      state;

    u_char                         *header_name_start;

    u_char                         *header_name_end;

    u_char                         *header_start;

    u_char                         *header_end;

    ngx_str_t                       addr;

    ngx_str_t                       port;

    ngx_str_t                       err;

    ngx_str_t                       errmsg;

    ngx_str_t                       errcode;

    time_t                          sleep;

    ngx_pool_t                     *pool;

};

static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);

static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);

static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx);

static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx);

static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);

static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx);

static void ngx_mail_auth_http_block_read(ngx_event_t *rev);

static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);

static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,

    ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);

static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,

    ngx_str_t *escaped);

static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);

static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_mail_auth_http_commands[] = {

    { ngx_string("auth_http"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_mail_auth_http,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("auth_http_timeout"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_auth_http_conf_t, timeout),

      NULL },

    { ngx_string("auth_http_header"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,

      ngx_mail_auth_http_header,

      NGX_MAIL_SRV_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_auth_http_module_ctx = {

    NULL,                                  /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_auth_http_create_conf,        /* create server configuration */

    ngx_mail_auth_http_merge_conf          /* merge server configuration */

};

‌ngx_module_t  ngx_mail_auth_http_module = {

    NGX_MODULE_V1,

    &ngx_mail_auth_http_module_ctx,        /* module context */

    ngx_mail_auth_http_commands,           /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t   ngx_mail_auth_http_method[] = {

    ngx_string("plain"),

    ngx_string("plain"),

    ngx_string("plain"),

    ngx_string("apop"),

    ngx_string("cram-md5"),

    ngx_string("none")

};

‌static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");

void

‌ngx_mail_auth_http_init(ngx_mail_session_t *s)

{

    ngx_int_t                   rc;

    ngx_pool_t                 *pool;

    ngx_mail_auth_http_ctx_t   *ctx;

    ngx_mail_auth_http_conf_t  *ahcf;

    s->connection->log->action = "in http auth state";

    pool = ngx_create_pool(2048, s->connection->log);

    if (pool == NULL) {

        ngx_mail_session_internal_server_error(s);

        return;

    }

    ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));

    if (ctx == NULL) {

        ngx_destroy_pool(pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    ctx->pool = pool;

    ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);

    ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);

    if (ctx->request == NULL) {

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);

    ctx->peer.sockaddr = ahcf->peer->sockaddr;

    ctx->peer.socklen = ahcf->peer->socklen;

    ctx->peer.name = &ahcf->peer->name;

    ctx->peer.get = ngx_event_get_peer;

    ctx->peer.log = s->connection->log;

    ctx->peer.log_error = NGX_ERROR_ERR;

    rc = ngx_event_connect_peer(&ctx->peer);

    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {

        if (ctx->peer.connection) {

            ngx_close_connection(ctx->peer.connection);

        }

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    ctx->peer.connection->data = s;

    ctx->peer.connection->pool = s->connection->pool;

    s->connection->read->handler = ngx_mail_auth_http_block_read;

    ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;

    ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;

    ctx->handler = ngx_mail_auth_http_ignore_status_line;

    ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);

    ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);

    if (rc == NGX_OK) {

        ngx_mail_auth_http_write_handler(ctx->peer.connection->write);

        return;

    }

}

static void

‌ngx_mail_auth_http_write_handler(ngx_event_t *wev)

{

    ssize_t                     n, size;

    ngx_connection_t           *c;

    ngx_mail_session_t         *s;

    ngx_mail_auth_http_ctx_t   *ctx;

    ngx_mail_auth_http_conf_t  *ahcf;

    c = wev->data;

    s = c->data;

    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,

                   "mail auth http write handler");

    if (wev->timedout) {

        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,

                      "auth http server %V timed out", ctx->peer.name);

        ngx_close_connection(c);

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    size = ctx->request->last - ctx->request->pos;

    n = ngx_send(c, ctx->request->pos, size);

    if (n == NGX_ERROR) {

        ngx_close_connection(c);

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    if (n > 0) {

        ctx->request->pos += n;

        if (n == size) {

            wev->handler = ngx_mail_auth_http_dummy_handler;

            if (wev->timer_set) {

                ngx_del_timer(wev);

            }

            if (ngx_handle_write_event(wev, 0) != NGX_OK) {

                ngx_close_connection(c);

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

            }

            return;

        }

    }

    if (!wev->timer_set) {

        ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);

        ngx_add_timer(wev, ahcf->timeout);

    }

}

static void

‌ngx_mail_auth_http_read_handler(ngx_event_t *rev)

{

    ssize_t                     n, size;

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_auth_http_ctx_t  *ctx;

    c = rev->data;

    s = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                   "mail auth http read handler");

    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,

                      "auth http server %V timed out", ctx->peer.name);

        ngx_close_connection(c);

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

    if (ctx->response == NULL) {

        ctx->response = ngx_create_temp_buf(ctx->pool, 1024);

        if (ctx->response == NULL) {

            ngx_close_connection(c);

            ngx_destroy_pool(ctx->pool);

            ngx_mail_session_internal_server_error(s);

            return;

        }

    }

    size = ctx->response->end - ctx->response->last;

    n = ngx_recv(c, ctx->response->pos, size);

    if (n > 0) {

        ctx->response->last += n;

        ctx->handler(s, ctx);

        return;

    }

    if (n == NGX_AGAIN) {

        return;

    }

    ngx_close_connection(c);

    ngx_destroy_pool(ctx->pool);

    ngx_mail_session_internal_server_error(s);

}

static void

‌ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx)

{

    u_char  *p, ch;

    enum  {

        sw_start = 0,

        sw_H,

        sw_HT,

        sw_HTT,

        sw_HTTP,

        sw_skip,

        sw_almost_done

    } state;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                   "mail auth http process status line");

    state = ctx->state;

    for (p = ctx->response->pos; p < ctx->response->last; p++) {

        ch = *p;

        switch (state) {

        /* "HTTP/" */

        case sw_start:

            if (ch == 'H') {

                state = sw_H;

                break;

            }

            goto next;

        case sw_H:

            if (ch == 'T') {

                state = sw_HT;

                break;

            }

            goto next;

        case sw_HT:

            if (ch == 'T') {

                state = sw_HTT;

                break;

            }

            goto next;

        case sw_HTT:

            if (ch == 'P') {

                state = sw_HTTP;

                break;

            }

            goto next;

        case sw_HTTP:

            if (ch == '/') {

                state = sw_skip;

                break;

            }

            goto next;

        /* any text until end of line */

        case sw_skip:

            switch (ch) {

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            }

            break;

        /* end of status line */

        case sw_almost_done:

            if (ch == LF) {

                goto done;

            }

            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                          "auth http server &V sent invalid response",

                          ctx->peer.name);

            ngx_close_connection(ctx->peer.connection);

            ngx_destroy_pool(ctx->pool);

            ngx_mail_session_internal_server_error(s);

            return;

        }

    }

    ctx->response->pos = p;

    ctx->state = state;

    return;

next:

    p = ctx->response->start - 1;

done:

    ctx->response->pos = p + 1;

    ctx->state = 0;

    ctx->handler = ngx_mail_auth_http_process_headers;

    ctx->handler(s, ctx);

}

static void

‌ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx)

{

    u_char               *p;

    time_t                timer;

    size_t                len, size;

    ngx_int_t             rc, port, n;

    ngx_addr_t           *peer;

    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6  *sin6;

#endif

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                   "mail auth http process headers");

    for ( ;; ) {

        rc = ngx_mail_auth_http_parse_header_line(s, ctx);

        if (rc == NGX_OK) {

#if (NGX_DEBUG)

            {

            ngx_str_t  key, value;

            key.len = ctx->header_name_end - ctx->header_name_start;

            key.data = ctx->header_name_start;

            value.len = ctx->header_end - ctx->header_start;

            value.data = ctx->header_start;

            ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                           "mail auth http header: \"%V: %V\"",

                           &key, &value);

            }

#endif

            len = ctx->header_name_end - ctx->header_name_start;

            if (len == sizeof("Auth-Status") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Status",

                                   sizeof("Auth-Status") - 1)

                   == 0)

            {

                len = ctx->header_end - ctx->header_start;

                if (len == 2

                    && ctx->header_start[0] == 'O'

                    && ctx->header_start[1] == 'K')

                {

                    continue;

                }

                if (len == 4

                    && ctx->header_start[0] == 'W'

                    && ctx->header_start[1] == 'A'

                    && ctx->header_start[2] == 'I'

                    && ctx->header_start[3] == 'T')

                {

                    s->auth_wait = 1;

                    continue;

                }

                ctx->errmsg.len = len;

                ctx->errmsg.data = ctx->header_start;

                switch (s->protocol) {

                case NGX_MAIL_POP3_PROTOCOL:

                    size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;

                    break;

                case NGX_MAIL_IMAP_PROTOCOL:

                    size = s->tag.len + sizeof("NO ") - 1 + len

                           + sizeof(CRLF) - 1;

                    break;

                default: /* NGX_MAIL_SMTP_PROTOCOL */

                    ctx->err = ctx->errmsg;

                    continue;

                }

                p = ngx_pnalloc(s->connection->pool, size);

                if (p == NULL) {

                    ngx_close_connection(ctx->peer.connection);

                    ngx_destroy_pool(ctx->pool);

                    ngx_mail_session_internal_server_error(s);

                    return;

                }

                ctx->err.data = p;

                switch (s->protocol) {

                case NGX_MAIL_POP3_PROTOCOL:

                    *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';

                    break;

                case NGX_MAIL_IMAP_PROTOCOL:

                    p = ngx_cpymem(p, s->tag.data, s->tag.len);

                    *p++ = 'N'; *p++ = 'O'; *p++ = ' ';

                    break;

                default: /* NGX_MAIL_SMTP_PROTOCOL */

                    break;

                }

                p = ngx_cpymem(p, ctx->header_start, len);

                *p++ = CR; *p++ = LF;

                ctx->err.len = p - ctx->err.data;

                continue;

            }

            if (len == sizeof("Auth-Server") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Server",

                                   sizeof("Auth-Server") - 1)

                    == 0)

            {

                ctx->addr.len = ctx->header_end - ctx->header_start;

                ctx->addr.data = ctx->header_start;

                continue;

            }

            if (len == sizeof("Auth-Port") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Port",

                                   sizeof("Auth-Port") - 1)

                   == 0)

            {

                ctx->port.len = ctx->header_end - ctx->header_start;

                ctx->port.data = ctx->header_start;

                continue;

            }

            if (len == sizeof("Auth-User") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-User",

                                   sizeof("Auth-User") - 1)

                   == 0)

            {

                s->login.len = ctx->header_end - ctx->header_start;

                s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);

                if (s->login.data == NULL) {

                    ngx_close_connection(ctx->peer.connection);

                    ngx_destroy_pool(ctx->pool);

                    ngx_mail_session_internal_server_error(s);

                    return;

                }

                ngx_memcpy(s->login.data, ctx->header_start, s->login.len);

                continue;

            }

            if (len == sizeof("Auth-Pass") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Pass",

                                   sizeof("Auth-Pass") - 1)

                   == 0)

            {

                s->passwd.len = ctx->header_end - ctx->header_start;

                s->passwd.data = ngx_pnalloc(s->connection->pool,

                                             s->passwd.len);

                if (s->passwd.data == NULL) {

                    ngx_close_connection(ctx->peer.connection);

                    ngx_destroy_pool(ctx->pool);

                    ngx_mail_session_internal_server_error(s);

                    return;

                }

                ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);

                continue;

            }

            if (len == sizeof("Auth-Wait") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Wait",

                                   sizeof("Auth-Wait") - 1)

                   == 0)

            {

                n = ngx_atoi(ctx->header_start,

                             ctx->header_end - ctx->header_start);

                if (n != NGX_ERROR) {

                    ctx->sleep = n;

                }

                continue;

            }

            if (len == sizeof("Auth-Error-Code") - 1

                && ngx_strncasecmp(ctx->header_name_start,

                                   (u_char *) "Auth-Error-Code",

                                   sizeof("Auth-Error-Code") - 1)

                   == 0)

            {

                ctx->errcode.len = ctx->header_end - ctx->header_start;

                ctx->errcode.data = ngx_pnalloc(s->connection->pool,

                                                ctx->errcode.len);

                if (ctx->errcode.data == NULL) {

                    ngx_close_connection(ctx->peer.connection);

                    ngx_destroy_pool(ctx->pool);

                    ngx_mail_session_internal_server_error(s);

                    return;

                }

                ngx_memcpy(ctx->errcode.data, ctx->header_start,

                           ctx->errcode.len);

                continue;

            }

            /* ignore other headers */

            continue;

        }

        if (rc == NGX_DONE) {

            ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                           "mail auth http header done");

            ngx_close_connection(ctx->peer.connection);

            if (ctx->err.len) {

                ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,

                              "client login failed: \"%V\"", &ctx->errmsg);

                if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {

                    if (ctx->errcode.len == 0) {

                        ctx->errcode = ngx_mail_smtp_errcode;

                    }

                    ctx->err.len = ctx->errcode.len + ctx->errmsg.len

                                   + sizeof(" " CRLF) - 1;

                    p = ngx_pnalloc(s->connection->pool, ctx->err.len);

                    if (p == NULL) {

                        ngx_destroy_pool(ctx->pool);

                        ngx_mail_session_internal_server_error(s);

                        return;

                    }

                    ctx->err.data = p;

                    p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);

                    *p++ = ' ';

                    p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);

                    *p++ = CR; *p = LF;

                }

                s->out = ctx->err;

                timer = ctx->sleep;

                ngx_destroy_pool(ctx->pool);

                if (timer == 0) {

                    s->quit = 1;

                    ngx_mail_send(s->connection->write);

                    return;

                }

                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

                s->connection->read->handler = ngx_mail_auth_sleep_handler;

                return;

            }

            if (s->auth_wait) {

                timer = ctx->sleep;

                ngx_destroy_pool(ctx->pool);

                if (timer == 0) {

                    ngx_mail_auth_http_init(s);

                    return;

                }

                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

                s->connection->read->handler = ngx_mail_auth_sleep_handler;

                return;

            }

            if (ctx->addr.len == 0 || ctx->port.len == 0) {

                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                              "auth http server %V did not send server or port",

                              ctx->peer.name);

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            if (s->passwd.data == NULL

                && s->protocol != NGX_MAIL_SMTP_PROTOCOL)

            {

                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                              "auth http server %V did not send password",

                              ctx->peer.name);

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));

            if (peer == NULL) {

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            rc = ngx_parse_addr(s->connection->pool, peer,

                                ctx->addr.data, ctx->addr.len);

            switch (rc) {

            case NGX_OK:

                break;

            case NGX_DECLINED:

                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                              "auth http server %V sent invalid server "

                              "address:\"%V\"",

                              ctx->peer.name, &ctx->addr);

                /* fall through */

            default:

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            port = ngx_atoi(ctx->port.data, ctx->port.len);

            if (port == NGX_ERROR || port < 1 || port > 65535) {

                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                              "auth http server %V sent invalid server "

                              "port:\"%V\"",

                              ctx->peer.name, &ctx->port);

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            switch (peer->sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

            case AF_INET6:

                sin6 = (struct sockaddr_in6 *) peer->sockaddr;

                sin6->sin6_port = htons((in_port_t) port);

                break;

#endif

            default: /* AF_INET */

                sin = (struct sockaddr_in *) peer->sockaddr;

                sin->sin_port = htons((in_port_t) port);

                break;

            }

            len = ctx->addr.len + 1 + ctx->port.len;

            peer->name.len = len;

            peer->name.data = ngx_pnalloc(s->connection->pool, len);

            if (peer->name.data == NULL) {

                ngx_destroy_pool(ctx->pool);

                ngx_mail_session_internal_server_error(s);

                return;

            }

            len = ctx->addr.len;

            ngx_memcpy(peer->name.data, ctx->addr.data, len);

            peer->name.data[len++] = ':';

            ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);

            ngx_destroy_pool(ctx->pool);

            ngx_mail_proxy_init(s, peer);

            return;

        }

        if (rc == NGX_AGAIN ) {

            return;

        }

        /* rc == NGX_ERROR */

        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                      "auth http server %V sent invalid header in response",

                      ctx->peer.name);

        ngx_close_connection(ctx->peer.connection);

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

        return;

    }

}

static void

‌ngx_mail_auth_sleep_handler(ngx_event_t *rev)

{

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");

    c = rev->data;

    s = c->data;

    if (rev->timedout) {

        rev->timedout = 0;

        if (s->auth_wait) {

            s->auth_wait = 0;

            ngx_mail_auth_http_init(s);

            return;

        }

        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

        rev->handler = cscf->protocol->auth_state;

        s->mail_state = 0;

        s->auth_method = NGX_MAIL_AUTH_PLAIN;

        c->log->action = "in auth state";

        ngx_mail_send(c->write);

        if (c->destroyed) {

            return;

        }

        ngx_add_timer(rev, cscf->timeout);

        if (rev->ready) {

            rev->handler(rev);

            return;

        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {

            ngx_mail_close_connection(c);

        }

        return;

    }

    if (rev->active) {

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {

            ngx_mail_close_connection(c);

        }

    }

}

static ngx_int_t

‌ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,

    ngx_mail_auth_http_ctx_t *ctx)

{

    u_char      c, ch, *p;

    enum {

        sw_start = 0,

        sw_name,

        sw_space_before_value,

        sw_value,

        sw_space_after_value,

        sw_almost_done,

        sw_header_almost_done

    } state;

    state = ctx->state;

    for (p = ctx->response->pos; p < ctx->response->last; p++) {

        ch = *p;

        switch (state) {

        /* first char */

        case sw_start:

            switch (ch) {

            case CR:

                ctx->header_end = p;

                state = sw_header_almost_done;

                break;

            case LF:

                ctx->header_end = p;

                goto header_done;

            default:

                state = sw_name;

                ctx->header_name_start = p;

                c = (u_char) (ch | 0x20);

                if (c >= 'a' && c <= 'z') {

                    break;

                }

                if (ch >= '0' && ch <= '9') {

                    break;

                }

                return NGX_ERROR;

            }

            break;

        /* header name */

        case sw_name:

            c = (u_char) (ch | 0x20);

            if (c >= 'a' && c <= 'z') {

                break;

            }

            if (ch == ':') {

                ctx->header_name_end = p;

                state = sw_space_before_value;

                break;

            }

            if (ch == '-') {

                break;

            }

            if (ch >= '0' && ch <= '9') {

                break;

            }

            if (ch == CR) {

                ctx->header_name_end = p;

                ctx->header_start = p;

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            }

            if (ch == LF) {

                ctx->header_name_end = p;

                ctx->header_start = p;

                ctx->header_end = p;

                goto done;

            }

            return NGX_ERROR;

        /* space* before header value */

        case sw_space_before_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                ctx->header_start = p;

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                ctx->header_start = p;

                ctx->header_end = p;

                goto done;

            default:

                ctx->header_start = p;

                state = sw_value;

                break;

            }

            break;

        /* header value */

        case sw_value:

            switch (ch) {

            case ' ':

                ctx->header_end = p;

                state = sw_space_after_value;

                break;

            case CR:

                ctx->header_end = p;

                state = sw_almost_done;

                break;

            case LF:

                ctx->header_end = p;

                goto done;

            }

            break;

        /* space* before end of header line */

        case sw_space_after_value:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                state = sw_value;

                break;

            }

            break;

        /* end of header line */

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                return NGX_ERROR;

            }

        /* end of header */

        case sw_header_almost_done:

            switch (ch) {

            case LF:

                goto header_done;

            default:

                return NGX_ERROR;

            }

        }

    }

    ctx->response->pos = p;

    ctx->state = state;

    return NGX_AGAIN;

done:

    ctx->response->pos = p + 1;

    ctx->state = sw_start;

    return NGX_OK;

header_done:

    ctx->response->pos = p + 1;

    ctx->state = sw_start;

    return NGX_DONE;

}

static void

‌ngx_mail_auth_http_block_read(ngx_event_t *rev)

{

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_auth_http_ctx_t  *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                   "mail auth http block read");

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        c = rev->data;

        s = c->data;

        ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

        ngx_close_connection(ctx->peer.connection);

        ngx_destroy_pool(ctx->pool);

        ngx_mail_session_internal_server_error(s);

    }

}

static void

‌ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)

{

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,

                   "mail auth http dummy handler");

}

static ngx_buf_t *

‌ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,

    ngx_mail_auth_http_conf_t *ahcf)

{

    size_t                     len;

    ngx_buf_t                 *b;

    ngx_str_t                  login, passwd;

    ngx_mail_core_srv_conf_t  *cscf;

    if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {

        return NULL;

    }

    if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {

        return NULL;

    }

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1

          + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1

          + sizeof("Auth-Method: ") - 1

                + ngx_mail_auth_http_method[s->auth_method].len

                + sizeof(CRLF) - 1

          + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1

          + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1

          + sizeof("Auth-Salt: ") - 1 + s->salt.len

          + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len

                + sizeof(CRLF) - 1

          + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN

                + sizeof(CRLF) - 1

          + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len

                + sizeof(CRLF) - 1

          + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1

          + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len

          + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len

          + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len

          + ahcf->header.len

          + sizeof(CRLF) - 1;

    b = ngx_create_temp_buf(pool, len);

    if (b == NULL) {

        return NULL;

    }

    b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);

    b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);

    b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,

                         sizeof(" HTTP/1.0" CRLF) - 1);

    b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);

    b->last = ngx_copy(b->last, ahcf->host_header.data,

                         ahcf->host_header.len);

    *b->last++ = CR; *b->last++ = LF;

    b->last = ngx_cpymem(b->last, "Auth-Method: ",

                         sizeof("Auth-Method: ") - 1);

    b->last = ngx_cpymem(b->last,

                         ngx_mail_auth_http_method[s->auth_method].data,

                         ngx_mail_auth_http_method[s->auth_method].len);

    *b->last++ = CR; *b->last++ = LF;

    b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);

    b->last = ngx_copy(b->last, login.data, login.len);

    *b->last++ = CR; *b->last++ = LF;

    b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);

    b->last = ngx_copy(b->last, passwd.data, passwd.len);

    *b->last++ = CR; *b->last++ = LF;

    if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {

        b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);

        b->last = ngx_copy(b->last, s->salt.data, s->salt.len);

        s->passwd.data = NULL;

    }

    b->last = ngx_cpymem(b->last, "Auth-Protocol: ",

                         sizeof("Auth-Protocol: ") - 1);

    b->last = ngx_cpymem(b->last, cscf->protocol->name.data,

                         cscf->protocol->name.len);

    *b->last++ = CR; *b->last++ = LF;

    b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,

                          s->login_attempt);

    b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);

    b->last = ngx_copy(b->last, s->connection->addr_text.data,

                       s->connection->addr_text.len);

    *b->last++ = CR; *b->last++ = LF;

    if (s->host.len) {

        b->last = ngx_cpymem(b->last, "Client-Host: ",

                             sizeof("Client-Host: ") - 1);

        b->last = ngx_copy(b->last, s->host.data, s->host.len);

        *b->last++ = CR; *b->last++ = LF;

    }

    if (s->auth_method == NGX_MAIL_AUTH_NONE) {

        /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */

        b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",

                             sizeof("Auth-SMTP-Helo: ") - 1);

        b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);

        *b->last++ = CR; *b->last++ = LF;

        b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",

                             sizeof("Auth-SMTP-From: ") - 1);

        b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);

        *b->last++ = CR; *b->last++ = LF;

        b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",

                             sizeof("Auth-SMTP-To: ") - 1);

        b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);

        *b->last++ = CR; *b->last++ = LF;

    }

    if (ahcf->header.len) {

        b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);

    }

    /* add "\r\n" at the header end */

    *b->last++ = CR; *b->last++ = LF;

#if (NGX_DEBUG_MAIL_PASSWD)

    {

    ngx_str_t  l;

    l.len = b->last - b->pos;

    l.data = b->pos;

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                   "mail auth http header:%N\"%V\"", &l);

    }

#endif

    return b;

}

static ngx_int_t

‌ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)

{

    u_char     *p;

    uintptr_t   n;

    n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);

    if (n == 0) {

        *escaped = *text;

        return NGX_OK;

    }

    escaped->len = text->len + n * 2;

    p = ngx_pnalloc(pool, escaped->len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);

    escaped->data = p;

    return NGX_OK;

}

static void *

‌ngx_mail_auth_http_create_conf(ngx_conf_t *cf)

{

    ngx_mail_auth_http_conf_t  *ahcf;

    ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));

    if (ahcf == NULL) {

        return NULL;

    }

    ahcf->timeout = NGX_CONF_UNSET_MSEC;

    ahcf->file = cf->conf_file->file.name.data;

    ahcf->line = cf->conf_file->line;

    return ahcf;

}

static char *

‌ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_auth_http_conf_t *prev = parent;

    ngx_mail_auth_http_conf_t *conf = child;

    u_char           *p;

    size_t            len;

    ngx_uint_t        i;

    ngx_table_elt_t  *header;

    if (conf->peer == NULL) {

        conf->peer = prev->peer;

        conf->host_header = prev->host_header;

        conf->uri = prev->uri;

        if (conf->peer == NULL) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"auth_http\" is defined for server in %s:%ui",

                          conf->file, conf->line);

            return NGX_CONF_ERROR;

        }

    }

    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);

    if (conf->headers == NULL) {

        conf->headers = prev->headers;

        conf->header = prev->header;

    }

    if (conf->headers && conf->header.len == 0) {

        len = 0;

        header = conf->headers->elts;

        for (i = 0; i < conf->headers->nelts; i++) {

            len += header[i].key.len + 2 + header[i].value.len + 2;

        }

        p = ngx_pnalloc(cf->pool, len);

        if (p == NULL) {

            return NGX_CONF_ERROR;

        }

        conf->header.len = len;

        conf->header.data = p;

        for (i = 0; i < conf->headers->nelts; i++) {

            p = ngx_cpymem(p, header[i].key.data, header[i].key.len);

            *p++ = ':'; *p++ = ' ';

            p = ngx_cpymem(p, header[i].value.data, header[i].value.len);

            *p++ = CR; *p++ = LF;

        }

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_auth_http_conf_t *ahcf = conf;

    ngx_str_t  *value;

    ngx_url_t   u;

    value = cf->args->elts;

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.default_port = 80;

    u.uri_part = 1;

    if (ngx_strncmp(u.url.data, "http://", 7) == 0) {

        u.url.len -= 7;

        u.url.data += 7;

    }

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "%s in auth_http \"%V\"", u.err, &u.url);

        }

        return NGX_CONF_ERROR;

    }

    ahcf->peer = u.addrs;

    if (u.family != AF_UNIX) {

        ahcf->host_header = u.host;

    } else {

        ngx_str_set(&ahcf->host_header, "localhost");

    }

    ahcf->uri = u.uri;

    if (ahcf->uri.len == 0) {

        ngx_str_set(&ahcf->uri, "/");

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_mail_auth_http_conf_t *ahcf = conf;

    ngx_str_t        *value;

    ngx_table_elt_t  *header;

    if (ahcf->headers == NULL) {

        ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));

        if (ahcf->headers == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    header = ngx_array_push(ahcf->headers);

    if (header == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    header->key = value[1];

    header->value = value[2];

    return NGX_CONF_OK;

}

src/mail/ngx_mail_proxy_module.c - nginx-1.7.9

Global variables defined

ngx_mail_proxy_commands

ngx_mail_proxy_module

ngx_mail_proxy_module_ctx

smtp_auth_ok

Data types defined

ngx_mail_proxy_conf_t

Functions defined

ngx_mail_proxy_block_read

ngx_mail_proxy_close_session

ngx_mail_proxy_create_conf

ngx_mail_proxy_dummy_handler

ngx_mail_proxy_handler

ngx_mail_proxy_imap_handler

ngx_mail_proxy_init

ngx_mail_proxy_internal_server_error

ngx_mail_proxy_merge_conf

ngx_mail_proxy_pop3_handler

ngx_mail_proxy_read_response

ngx_mail_proxy_smtp_handler

ngx_mail_proxy_upstream_error

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

#include <ngx_mail.h>

typedef struct {

    ngx_flag_t  enable;

    ngx_flag_t  pass_error_message;

    ngx_flag_t  xclient;

    size_t      buffer_size;

    ngx_msec_t  timeout;

‌} ngx_mail_proxy_conf_t;

static void ngx_mail_proxy_block_read(ngx_event_t *rev);

static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);

static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);

static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);

static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);

static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,

    ngx_uint_t state);

static void ngx_mail_proxy_handler(ngx_event_t *ev);

static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);

static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);

static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);

static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);

static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

‌static ngx_command_t  ngx_mail_proxy_commands[] = {

    { ngx_string("proxy"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_proxy_conf_t, enable),

      NULL },

    { ngx_string("proxy_buffer"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_proxy_conf_t, buffer_size),

      NULL },

    { ngx_string("proxy_timeout"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_proxy_conf_t, timeout),

      NULL },

    { ngx_string("proxy_pass_error_message"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_proxy_conf_t, pass_error_message),

      NULL },

    { ngx_string("xclient"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_proxy_conf_t, xclient),

      NULL },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_proxy_module_ctx = {

    NULL,                                  /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_proxy_create_conf,            /* create server configuration */

    ngx_mail_proxy_merge_conf              /* merge server configuration */

};

‌ngx_module_t  ngx_mail_proxy_module = {

    NGX_MODULE_V1,

    &ngx_mail_proxy_module_ctx,            /* module context */

    ngx_mail_proxy_commands,               /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static u_char  smtp_auth_ok[] = "235 2.0.0 OK" CRLF;

void

‌ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)

{

    int                        keepalive;

    ngx_int_t                  rc;

    ngx_mail_proxy_ctx_t      *p;

    ngx_mail_proxy_conf_t     *pcf;

    ngx_mail_core_srv_conf_t  *cscf;

    s->connection->log->action = "connecting to upstream";

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    if (cscf->so_keepalive) {

        keepalive = 1;

        if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE,

                       (const void *) &keepalive, sizeof(int))

                == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,

                          "setsockopt(SO_KEEPALIVE) failed");

        }

    }

    p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));

    if (p == NULL) {

        ngx_mail_session_internal_server_error(s);

        return;

    }

    s->proxy = p;

    p->upstream.sockaddr = peer->sockaddr;

    p->upstream.socklen = peer->socklen;

    p->upstream.name = &peer->name;

    p->upstream.get = ngx_event_get_peer;

    p->upstream.log = s->connection->log;

    p->upstream.log_error = NGX_ERROR_ERR;

    rc = ngx_event_connect_peer(&p->upstream);

    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    ngx_add_timer(p->upstream.connection->read, cscf->timeout);

    p->upstream.connection->data = s;

    p->upstream.connection->pool = s->connection->pool;

    s->connection->read->handler = ngx_mail_proxy_block_read;

    p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;

    pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

    s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,

                                           pcf->buffer_size);

    if (s->proxy->buffer == NULL) {

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    s->out.len = 0;

    switch (s->protocol) {

    case NGX_MAIL_POP3_PROTOCOL:

        p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;

        s->mail_state = ngx_pop3_start;

        break;

    case NGX_MAIL_IMAP_PROTOCOL:

        p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;

        s->mail_state = ngx_imap_start;

        break;

    default: /* NGX_MAIL_SMTP_PROTOCOL */

        p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;

        s->mail_state = ngx_smtp_start;

        break;

    }

}

static void

‌ngx_mail_proxy_block_read(ngx_event_t *rev)

{

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        c = rev->data;

        s = c->data;

        ngx_mail_proxy_close_session(s);

    }

}

static void

‌ngx_mail_proxy_pop3_handler(ngx_event_t *rev)

{

    u_char                 *p;

    ngx_int_t               rc;

    ngx_str_t               line;

    ngx_connection_t       *c;

    ngx_mail_session_t     *s;

    ngx_mail_proxy_conf_t  *pcf;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                   "mail proxy pop3 auth handler");

    c = rev->data;

    s = c->data;

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                      "upstream timed out");

        c->timedout = 1;

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    rc = ngx_mail_proxy_read_response(s, 0);

    if (rc == NGX_AGAIN) {

        return;

    }

    if (rc == NGX_ERROR) {

        ngx_mail_proxy_upstream_error(s);

        return;

    }

    switch (s->mail_state) {

    case ngx_pop3_start:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");

        s->connection->log->action = "sending user name to upstream";

        line.len = sizeof("USER ")  - 1 + s->login.len + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);

        p = ngx_cpymem(p, s->login.data, s->login.len);

        *p++ = CR; *p = LF;

        s->mail_state = ngx_pop3_user;

        break;

    case ngx_pop3_user:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");

        s->connection->log->action = "sending password to upstream";

        line.len = sizeof("PASS ")  - 1 + s->passwd.len + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);

        p = ngx_cpymem(p, s->passwd.data, s->passwd.len);

        *p++ = CR; *p = LF;

        s->mail_state = ngx_pop3_passwd;

        break;

    case ngx_pop3_passwd:

        s->connection->read->handler = ngx_mail_proxy_handler;

        s->connection->write->handler = ngx_mail_proxy_handler;

        rev->handler = ngx_mail_proxy_handler;

        c->write->handler = ngx_mail_proxy_handler;

        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

        ngx_add_timer(s->connection->read, pcf->timeout);

        ngx_del_timer(c->read);

        c->log->action = NULL;

        ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

        ngx_mail_proxy_handler(s->connection->write);

        return;

    default:

#if (NGX_SUPPRESS_WARN)

        ngx_str_null(&line);

#endif

        break;

    }

    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {

        /*

         * we treat the incomplete sending as NGX_ERROR

         * because it is very strange here

         */

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    s->proxy->buffer->pos = s->proxy->buffer->start;

    s->proxy->buffer->last = s->proxy->buffer->start;

}

static void

‌ngx_mail_proxy_imap_handler(ngx_event_t *rev)

{

    u_char                 *p;

    ngx_int_t               rc;

    ngx_str_t               line;

    ngx_connection_t       *c;

    ngx_mail_session_t     *s;

    ngx_mail_proxy_conf_t  *pcf;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                   "mail proxy imap auth handler");

    c = rev->data;

    s = c->data;

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                      "upstream timed out");

        c->timedout = 1;

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    rc = ngx_mail_proxy_read_response(s, s->mail_state);

    if (rc == NGX_AGAIN) {

        return;

    }

    if (rc == NGX_ERROR) {

        ngx_mail_proxy_upstream_error(s);

        return;

    }

    switch (s->mail_state) {

    case ngx_imap_start:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send login");

        s->connection->log->action = "sending LOGIN command to upstream";

        line.len = s->tag.len + sizeof("LOGIN ") - 1

                   + 1 + NGX_SIZE_T_LEN + 1 + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,

                               &s->tag, s->login.len)

                   - line.data;

        s->mail_state = ngx_imap_login;

        break;

    case ngx_imap_login:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");

        s->connection->log->action = "sending user name to upstream";

        line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,

                               &s->login, s->passwd.len)

                   - line.data;

        s->mail_state = ngx_imap_user;

        break;

    case ngx_imap_user:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send passwd");

        s->connection->log->action = "sending password to upstream";

        line.len = s->passwd.len + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);

        *p++ = CR; *p = LF;

        s->mail_state = ngx_imap_passwd;

        break;

    case ngx_imap_passwd:

        s->connection->read->handler = ngx_mail_proxy_handler;

        s->connection->write->handler = ngx_mail_proxy_handler;

        rev->handler = ngx_mail_proxy_handler;

        c->write->handler = ngx_mail_proxy_handler;

        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

        ngx_add_timer(s->connection->read, pcf->timeout);

        ngx_del_timer(c->read);

        c->log->action = NULL;

        ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

        ngx_mail_proxy_handler(s->connection->write);

        return;

    default:

#if (NGX_SUPPRESS_WARN)

        ngx_str_null(&line);

#endif

        break;

    }

    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {

        /*

         * we treat the incomplete sending as NGX_ERROR

         * because it is very strange here

         */

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    s->proxy->buffer->pos = s->proxy->buffer->start;

    s->proxy->buffer->last = s->proxy->buffer->start;

}

static void

‌ngx_mail_proxy_smtp_handler(ngx_event_t *rev)

{

    u_char                    *p;

    ngx_int_t                  rc;

    ngx_str_t                  line;

    ngx_buf_t                 *b;

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_proxy_conf_t     *pcf;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                   "mail proxy smtp auth handler");

    c = rev->data;

    s = c->data;

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                      "upstream timed out");

        c->timedout = 1;

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    rc = ngx_mail_proxy_read_response(s, s->mail_state);

    if (rc == NGX_AGAIN) {

        return;

    }

    if (rc == NGX_ERROR) {

        ngx_mail_proxy_upstream_error(s);

        return;

    }

    switch (s->mail_state) {

    case ngx_smtp_start:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");

        s->connection->log->action = "sending HELO/EHLO to upstream";

        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

        line.len = sizeof("HELO ")  - 1 + cscf->server_name.len + 2;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

        p = ngx_cpymem(line.data,

                       ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),

                       sizeof("HELO ") - 1);

        p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);

        *p++ = CR; *p = LF;

        if (pcf->xclient) {

            s->mail_state = ngx_smtp_helo_xclient;

        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {

            s->mail_state = ngx_smtp_helo_from;

        } else {

            s->mail_state = ngx_smtp_helo;

        }

        break;

    case ngx_smtp_helo_xclient:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send xclient");

        s->connection->log->action = "sending XCLIENT to upstream";

        line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="

                          CRLF) - 1

                   + s->connection->addr_text.len + s->login.len + s->host.len;

#if (NGX_HAVE_INET6)

        if (s->connection->sockaddr->sa_family == AF_INET6) {

            line.len += sizeof("IPV6:") - 1;

        }

#endif

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);

#if (NGX_HAVE_INET6)

        if (s->connection->sockaddr->sa_family == AF_INET6) {

            p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);

        }

#endif

        p = ngx_copy(p, s->connection->addr_text.data,

                     s->connection->addr_text.len);

        if (s->login.len) {

            p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);

            p = ngx_copy(p, s->login.data, s->login.len);

        }

        p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);

        p = ngx_copy(p, s->host.data, s->host.len);

        *p++ = CR; *p++ = LF;

        line.len = p - line.data;

        if (s->smtp_helo.len) {

            s->mail_state = ngx_smtp_xclient_helo;

        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {

            s->mail_state = ngx_smtp_xclient_from;

        } else {

            s->mail_state = ngx_smtp_xclient;

        }

        break;

    case ngx_smtp_xclient_helo:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send client ehlo");

        s->connection->log->action = "sending client HELO/EHLO to upstream";

        line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        line.len = ngx_sprintf(line.data,

                       ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),

                       &s->smtp_helo)

                   - line.data;

        s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?

                            ngx_smtp_helo_from : ngx_smtp_helo;

        break;

    case ngx_smtp_helo_from:

    case ngx_smtp_xclient_from:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send mail from");

        s->connection->log->action = "sending MAIL FROM to upstream";

        line.len = s->smtp_from.len + sizeof(CRLF) - 1;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);

        *p++ = CR; *p = LF;

        s->mail_state = ngx_smtp_from;

        break;

    case ngx_smtp_from:

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,

                       "mail proxy send rcpt to");

        s->connection->log->action = "sending RCPT TO to upstream";

        line.len = s->smtp_to.len + sizeof(CRLF) - 1;

        line.data = ngx_pnalloc(c->pool, line.len);

        if (line.data == NULL) {

            ngx_mail_proxy_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);

        *p++ = CR; *p = LF;

        s->mail_state = ngx_smtp_to;

        break;

    case ngx_smtp_helo:

    case ngx_smtp_xclient:

    case ngx_smtp_to:

        b = s->proxy->buffer;

        if (s->auth_method == NGX_MAIL_AUTH_NONE) {

            b->pos = b->start;

        } else {

            ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);

            b->last = b->start + sizeof(smtp_auth_ok) - 1;

        }

        s->connection->read->handler = ngx_mail_proxy_handler;

        s->connection->write->handler = ngx_mail_proxy_handler;

        rev->handler = ngx_mail_proxy_handler;

        c->write->handler = ngx_mail_proxy_handler;

        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

        ngx_add_timer(s->connection->read, pcf->timeout);

        ngx_del_timer(c->read);

        c->log->action = NULL;

        ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");

        if (s->buffer->pos == s->buffer->last) {

            ngx_mail_proxy_handler(s->connection->write);

        } else {

            ngx_mail_proxy_handler(c->write);

        }

        return;

    default:

#if (NGX_SUPPRESS_WARN)

        ngx_str_null(&line);

#endif

        break;

    }

    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {

        /*

         * we treat the incomplete sending as NGX_ERROR

         * because it is very strange here

         */

        ngx_mail_proxy_internal_server_error(s);

        return;

    }

    s->proxy->buffer->pos = s->proxy->buffer->start;

    s->proxy->buffer->last = s->proxy->buffer->start;

}

static void

‌ngx_mail_proxy_dummy_handler(ngx_event_t *wev)

{

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");

    if (ngx_handle_write_event(wev, 0) != NGX_OK) {

        c = wev->data;

        s = c->data;

        ngx_mail_proxy_close_session(s);

    }

}

static ngx_int_t

‌ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)

{

    u_char                 *p, *m;

    ssize_t                 n;

    ngx_buf_t              *b;

    ngx_mail_proxy_conf_t  *pcf;

    s->connection->log->action = "reading response from upstream";

    b = s->proxy->buffer;

    n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,

                                            b->last, b->end - b->last);

    if (n == NGX_ERROR || n == 0) {

        return NGX_ERROR;

    }

    if (n == NGX_AGAIN) {

        return NGX_AGAIN;

    }

    b->last += n;

    if (b->last - b->pos < 4) {

        return NGX_AGAIN;

    }

    if (*(b->last - 2) != CR || *(b->last - 1) != LF) {

        if (b->last == b->end) {

            *(b->last - 1) = '\0';

            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                          "upstream sent too long response line: \"%s\"",

                          b->pos);

            return NGX_ERROR;

        }

        return NGX_AGAIN;

    }

    p = b->pos;

    switch (s->protocol) {

    case NGX_MAIL_POP3_PROTOCOL:

        if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {

            return NGX_OK;

        }

        break;

    case NGX_MAIL_IMAP_PROTOCOL:

        switch (state) {

        case ngx_imap_start:

            if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {

                return NGX_OK;

            }

            break;

        case ngx_imap_login:

        case ngx_imap_user:

            if (p[0] == '+') {

                return NGX_OK;

            }

            break;

        case ngx_imap_passwd:

            if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {

                p += s->tag.len;

                if (p[0] == 'O' && p[1] == 'K') {

                    return NGX_OK;

                }

            }

            break;

        }

        break;

    default: /* NGX_MAIL_SMTP_PROTOCOL */

        if (p[3] == '-') {

            /* multiline reply, check if we got last line */

            m = b->last - (sizeof(CRLF "200" CRLF) - 1);

            while (m > p) {

                if (m[0] == CR && m[1] == LF) {

                    break;

                }

                m--;

            }

            if (m <= p || m[5] == '-') {

                return NGX_AGAIN;

            }

        }

        switch (state) {

        case ngx_smtp_start:

            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {

                return NGX_OK;

            }

            break;

        case ngx_smtp_helo:

        case ngx_smtp_helo_xclient:

        case ngx_smtp_helo_from:

        case ngx_smtp_from:

            if (p[0] == '2' && p[1] == '5' && p[2] == '0') {

                return NGX_OK;

            }

            break;

        case ngx_smtp_xclient:

        case ngx_smtp_xclient_from:

        case ngx_smtp_xclient_helo:

            if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {

                return NGX_OK;

            }

            break;

        case ngx_smtp_to:

            return NGX_OK;

        }

        break;

    }

    pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

    if (pcf->pass_error_message == 0) {

        *(b->last - 2) = '\0';

        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,

                      "upstream sent invalid response: \"%s\"", p);

        return NGX_ERROR;

    }

    s->out.len = b->last - p - 2;

    s->out.data = p;

    ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,

                  "upstream sent invalid response: \"%V\"", &s->out);

    s->out.len = b->last - b->pos;

    s->out.data = b->pos;

    return NGX_ERROR;

}

static void

‌ngx_mail_proxy_handler(ngx_event_t *ev)

{

    char                   *action, *recv_action, *send_action;

    size_t                  size;

    ssize_t                 n;

    ngx_buf_t              *b;

    ngx_uint_t              do_write;

    ngx_connection_t       *c, *src, *dst;

    ngx_mail_session_t     *s;

    ngx_mail_proxy_conf_t  *pcf;

    c = ev->data;

    s = c->data;

    if (ev->timedout) {

        c->log->action = "proxying";

        if (c == s->connection) {

            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                          "client timed out");

            c->timedout = 1;

        } else {

            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,

                          "upstream timed out");

        }

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (c == s->connection) {

        if (ev->write) {

            recv_action = "proxying and reading from upstream";

            send_action = "proxying and sending to client";

            src = s->proxy->upstream.connection;

            dst = c;

            b = s->proxy->buffer;

        } else {

            recv_action = "proxying and reading from client";

            send_action = "proxying and sending to upstream";

            src = c;

            dst = s->proxy->upstream.connection;

            b = s->buffer;

        }

    } else {

        if (ev->write) {

            recv_action = "proxying and reading from client";

            send_action = "proxying and sending to upstream";

            src = s->connection;

            dst = c;

            b = s->buffer;

        } else {

            recv_action = "proxying and reading from upstream";

            send_action = "proxying and sending to client";

            src = c;

            dst = s->connection;

            b = s->proxy->buffer;

        }

    }

    do_write = ev->write ? 1 : 0;

    ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,

                   "mail proxy handler: %d, #%d > #%d",

                   do_write, src->fd, dst->fd);

    for ( ;; ) {

        if (do_write) {

            size = b->last - b->pos;

            if (size && dst->write->ready) {

                c->log->action = send_action;

                n = dst->send(dst, b->pos, size);

                if (n == NGX_ERROR) {

                    ngx_mail_proxy_close_session(s);

                    return;

                }

                if (n > 0) {

                    b->pos += n;

                    if (b->pos == b->last) {

                        b->pos = b->start;

                        b->last = b->start;

                    }

                }

            }

        }

        size = b->end - b->last;

        if (size && src->read->ready) {

            c->log->action = recv_action;

            n = src->recv(src, b->last, size);

            if (n == NGX_AGAIN || n == 0) {

                break;

            }

            if (n > 0) {

                do_write = 1;

                b->last += n;

                continue;

            }

            if (n == NGX_ERROR) {

                src->read->eof = 1;

            }

        }

        break;

    }

    c->log->action = "proxying";

    if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)

        || (s->proxy->upstream.connection->read->eof

            && s->proxy->buffer->pos == s->proxy->buffer->last)

        || (s->connection->read->eof

            && s->proxy->upstream.connection->read->eof))

    {

        action = c->log->action;

        c->log->action = NULL;

        ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");

        c->log->action = action;

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (ngx_handle_write_event(src->write, 0) != NGX_OK) {

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (ngx_handle_read_event(src->read, 0) != NGX_OK) {

        ngx_mail_proxy_close_session(s);

        return;

    }

    if (c == s->connection) {

        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);

        ngx_add_timer(c->read, pcf->timeout);

    }

}

static void

‌ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)

{

    if (s->proxy->upstream.connection) {

        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                       "close mail proxy connection: %d",

                       s->proxy->upstream.connection->fd);

        ngx_close_connection(s->proxy->upstream.connection);

    }

    if (s->out.len == 0) {

        ngx_mail_session_internal_server_error(s);

        return;

    }

    s->quit = 1;

    ngx_mail_send(s->connection->write);

}

static void

‌ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)

{

    if (s->proxy->upstream.connection) {

        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                       "close mail proxy connection: %d",

                       s->proxy->upstream.connection->fd);

        ngx_close_connection(s->proxy->upstream.connection);

    }

    ngx_mail_session_internal_server_error(s);

}

static void

‌ngx_mail_proxy_close_session(ngx_mail_session_t *s)

{

    if (s->proxy->upstream.connection) {

        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,

                       "close mail proxy connection: %d",

                       s->proxy->upstream.connection->fd);

        ngx_close_connection(s->proxy->upstream.connection);

    }

    ngx_mail_close_connection(s->connection);

}

static void *

‌ngx_mail_proxy_create_conf(ngx_conf_t *cf)

{

    ngx_mail_proxy_conf_t  *pcf;

    pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));

    if (pcf == NULL) {

        return NULL;

    }

    pcf->enable = NGX_CONF_UNSET;

    pcf->pass_error_message = NGX_CONF_UNSET;

    pcf->xclient = NGX_CONF_UNSET;

    pcf->buffer_size = NGX_CONF_UNSET_SIZE;

    pcf->timeout = NGX_CONF_UNSET_MSEC;

    return pcf;

}

static char *

‌ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_proxy_conf_t *prev = parent;

    ngx_mail_proxy_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);

    ngx_conf_merge_value(conf->xclient, prev->xclient, 1);

    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);

    return NGX_CONF_OK;

}

src/mail/ngx_mail_parse.c - nginx-1.7.9

Functions defined

ngx_mail_auth_parse

ngx_mail_imap_parse_command

ngx_mail_pop3_parse_command

ngx_mail_smtp_parse_command

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_pop3_module.h>

#include <ngx_mail_imap_module.h>

#include <ngx_mail_smtp_module.h>

ngx_int_t

‌ngx_mail_pop3_parse_command(ngx_mail_session_t *s)

{

    u_char      ch, *p, *c, c0, c1, c2, c3;

    ngx_str_t  *arg;

    enum {

        sw_start = 0,

        sw_spaces_before_argument,

        sw_argument,

        sw_almost_done

    } state;

    state = s->state;

    for (p = s->buffer->pos; p < s->buffer->last; p++) {

        ch = *p;

        switch (state) {

        /* POP3 command */

        case sw_start:

            if (ch == ' ' || ch == CR || ch == LF) {

                c = s->buffer->start;

                if (p - c == 4) {

                    c0 = ngx_toupper(c[0]);

                    c1 = ngx_toupper(c[1]);

                    c2 = ngx_toupper(c[2]);

                    c3 = ngx_toupper(c[3]);

                    if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')

                    {

                        s->command = NGX_POP3_USER;

                    } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')

                    {

                        s->command = NGX_POP3_PASS;

                    } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')

                    {

                        s->command = NGX_POP3_APOP;

                    } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')

                    {

                        s->command = NGX_POP3_QUIT;

                    } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')

                    {

                        s->command = NGX_POP3_CAPA;

                    } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')

                    {

                        s->command = NGX_POP3_AUTH;

                    } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')

                    {

                        s->command = NGX_POP3_NOOP;

#if (NGX_MAIL_SSL)

                    } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')

                    {

                        s->command = NGX_POP3_STLS;

#endif

                    } else {

                        goto invalid;

                    }

                } else {

                    goto invalid;

                }

                switch (ch) {

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            }

            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {

                goto invalid;

            }

            break;

        case sw_spaces_before_argument:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                s->arg_end = p;

                break;

            case LF:

                s->arg_end = p;

                goto done;

            default:

                if (s->args.nelts <= 2) {

                    state = sw_argument;

                    s->arg_start = p;

                    break;

                }

                goto invalid;

            }

            break;

        case sw_argument:

            switch (ch) {

            case ' ':

                /*

                 * the space should be considered as part of the at username

                 * or password, but not of argument in other commands

                 */

                if (s->command == NGX_POP3_USER

                    || s->command == NGX_POP3_PASS)

                {

                    break;

                }

                /* fall through */

            case CR:

            case LF:

                arg = ngx_array_push(&s->args);

                if (arg == NULL) {

                    return NGX_ERROR;

                }

                arg->len = p - s->arg_start;

                arg->data = s->arg_start;

                s->arg_start = NULL;

                switch (ch) {

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            default:

                break;

            }

            break;

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                goto invalid;

            }

        }

    }

    s->buffer->pos = p;

    s->state = state;

    return NGX_AGAIN;

done:

    s->buffer->pos = p + 1;

    if (s->arg_start) {

        arg = ngx_array_push(&s->args);

        if (arg == NULL) {

            return NGX_ERROR;

        }

        arg->len = s->arg_end - s->arg_start;

        arg->data = s->arg_start;

        s->arg_start = NULL;

    }

    s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;

    return NGX_OK;

invalid:

    s->state = sw_start;

    s->arg_start = NULL;

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

ngx_int_t

‌ngx_mail_imap_parse_command(ngx_mail_session_t *s)

{

    u_char      ch, *p, *c;

    ngx_str_t  *arg;

    enum {

        sw_start = 0,

        sw_spaces_before_command,

        sw_command,

        sw_spaces_before_argument,

        sw_argument,

        sw_backslash,

        sw_literal,

        sw_no_sync_literal_argument,

        sw_start_literal_argument,

        sw_literal_argument,

        sw_end_literal_argument,

        sw_almost_done

    } state;

    state = s->state;

    for (p = s->buffer->pos; p < s->buffer->last; p++) {

        ch = *p;

        switch (state) {

        /* IMAP tag */

        case sw_start:

            switch (ch) {

            case ' ':

                s->tag.len = p - s->buffer->start + 1;

                s->tag.data = s->buffer->start;

                state = sw_spaces_before_command;

                break;

            case CR:

                s->state = sw_start;

                return NGX_MAIL_PARSE_INVALID_COMMAND;

            case LF:

                s->state = sw_start;

                return NGX_MAIL_PARSE_INVALID_COMMAND;

            }

            break;

        case sw_spaces_before_command:

            switch (ch) {

            case ' ':

                break;

            case CR:

                s->state = sw_start;

                return NGX_MAIL_PARSE_INVALID_COMMAND;

            case LF:

                s->state = sw_start;

                return NGX_MAIL_PARSE_INVALID_COMMAND;

            default:

                s->cmd_start = p;

                state = sw_command;

                break;

            }

            break;

        case sw_command:

            if (ch == ' ' || ch == CR || ch == LF) {

                c = s->cmd_start;

                switch (p - c) {

                case 4:

                    if ((c[0] == 'N' || c[0] == 'n')

                        && (c[1] == 'O'|| c[1] == 'o')

                        && (c[2] == 'O'|| c[2] == 'o')

                        && (c[3] == 'P'|| c[3] == 'p'))

                    {

                        s->command = NGX_IMAP_NOOP;

                    } else {

                        goto invalid;

                    }

                    break;

                case 5:

                    if ((c[0] == 'L'|| c[0] == 'l')

                        && (c[1] == 'O'|| c[1] == 'o')

                        && (c[2] == 'G'|| c[2] == 'g')

                        && (c[3] == 'I'|| c[3] == 'i')

                        && (c[4] == 'N'|| c[4] == 'n'))

                    {

                        s->command = NGX_IMAP_LOGIN;

                    } else {

                        goto invalid;

                    }

                    break;

                case 6:

                    if ((c[0] == 'L'|| c[0] == 'l')

                        && (c[1] == 'O'|| c[1] == 'o')

                        && (c[2] == 'G'|| c[2] == 'g')

                        && (c[3] == 'O'|| c[3] == 'o')

                        && (c[4] == 'U'|| c[4] == 'u')

                        && (c[5] == 'T'|| c[5] == 't'))

                    {

                        s->command = NGX_IMAP_LOGOUT;

                    } else {

                        goto invalid;

                    }

                    break;

#if (NGX_MAIL_SSL)

                case 8:

                    if ((c[0] == 'S'|| c[0] == 's')

                        && (c[1] == 'T'|| c[1] == 't')

                        && (c[2] == 'A'|| c[2] == 'a')

                        && (c[3] == 'R'|| c[3] == 'r')

                        && (c[4] == 'T'|| c[4] == 't')

                        && (c[5] == 'T'|| c[5] == 't')

                        && (c[6] == 'L'|| c[6] == 'l')

                        && (c[7] == 'S'|| c[7] == 's'))

                    {

                        s->command = NGX_IMAP_STARTTLS;

                    } else {

                        goto invalid;

                    }

                    break;

#endif

                case 10:

                    if ((c[0] == 'C'|| c[0] == 'c')

                        && (c[1] == 'A'|| c[1] == 'a')

                        && (c[2] == 'P'|| c[2] == 'p')

                        && (c[3] == 'A'|| c[3] == 'a')

                        && (c[4] == 'B'|| c[4] == 'b')

                        && (c[5] == 'I'|| c[5] == 'i')

                        && (c[6] == 'L'|| c[6] == 'l')

                        && (c[7] == 'I'|| c[7] == 'i')

                        && (c[8] == 'T'|| c[8] == 't')

                        && (c[9] == 'Y'|| c[9] == 'y'))

                    {

                        s->command = NGX_IMAP_CAPABILITY;

                    } else {

                        goto invalid;

                    }

                    break;

                case 12:

                    if ((c[0] == 'A'|| c[0] == 'a')

                        && (c[1] == 'U'|| c[1] == 'u')

                        && (c[2] == 'T'|| c[2] == 't')

                        && (c[3] == 'H'|| c[3] == 'h')

                        && (c[4] == 'E'|| c[4] == 'e')

                        && (c[5] == 'N'|| c[5] == 'n')

                        && (c[6] == 'T'|| c[6] == 't')

                        && (c[7] == 'I'|| c[7] == 'i')

                        && (c[8] == 'C'|| c[8] == 'c')

                        && (c[9] == 'A'|| c[9] == 'a')

                        && (c[10] == 'T'|| c[10] == 't')

                        && (c[11] == 'E'|| c[11] == 'e'))

                    {

                        s->command = NGX_IMAP_AUTHENTICATE;

                    } else {

                        goto invalid;

                    }

                    break;

                default:

                    goto invalid;

                }

                switch (ch) {

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            }

            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {

                goto invalid;

            }

            break;

        case sw_spaces_before_argument:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                s->arg_end = p;

                break;

            case LF:

                s->arg_end = p;

                goto done;

            case '"':

                if (s->args.nelts <= 2) {

                    s->quoted = 1;

                    s->arg_start = p + 1;

                    state = sw_argument;

                    break;

                }

                goto invalid;

            case '{':

                if (s->args.nelts <= 2) {

                    state = sw_literal;

                    break;

                }

                goto invalid;

            default:

                if (s->args.nelts <= 2) {

                    s->arg_start = p;

                    state = sw_argument;

                    break;

                }

                goto invalid;

            }

            break;

        case sw_argument:

            if (ch == ' ' && s->quoted) {

                break;

            }

            switch (ch) {

            case '"':

                if (!s->quoted) {

                    break;

                }

                s->quoted = 0;

                /* fall through */

            case ' ':

            case CR:

            case LF:

                arg = ngx_array_push(&s->args);

                if (arg == NULL) {

                    return NGX_ERROR;

                }

                arg->len = p - s->arg_start;

                arg->data = s->arg_start;

                s->arg_start = NULL;

                switch (ch) {

                case '"':

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            case '\\':

                if (s->quoted) {

                    s->backslash = 1;

                    state = sw_backslash;

                }

                break;

            }

            break;

        case sw_backslash:

            switch (ch) {

            case CR:

            case LF:

                goto invalid;

            default:

                state = sw_argument;

            }

            break;

        case sw_literal:

            if (ch >= '0' && ch <= '9') {

                s->literal_len = s->literal_len * 10 + (ch - '0');

                break;

            }

            if (ch == '}') {

                state = sw_start_literal_argument;

                break;

            }

            if (ch == '+') {

                state = sw_no_sync_literal_argument;

                break;

            }

            goto invalid;

        case sw_no_sync_literal_argument:

            if (ch == '}') {

                s->no_sync_literal = 1;

                state = sw_start_literal_argument;

                break;

            }

            goto invalid;

        case sw_start_literal_argument:

            switch (ch) {

            case CR:

                break;

            case LF:

                s->buffer->pos = p + 1;

                s->arg_start = p + 1;

                if (s->no_sync_literal == 0) {

                    s->state = sw_literal_argument;

                    return NGX_IMAP_NEXT;

                }

                state = sw_literal_argument;

                s->no_sync_literal = 0;

                break;

            default:

                goto invalid;

            }

            break;

        case sw_literal_argument:

            if (s->literal_len && --s->literal_len) {

                break;

            }

            arg = ngx_array_push(&s->args);

            if (arg == NULL) {

                return NGX_ERROR;

            }

            arg->len = p + 1 - s->arg_start;

            arg->data = s->arg_start;

            s->arg_start = NULL;

            state = sw_end_literal_argument;

            break;

        case sw_end_literal_argument:

            switch (ch) {

            case '{':

                if (s->args.nelts <= 2) {

                    state = sw_literal;

                    break;

                }

                goto invalid;

            case CR:

                state = sw_almost_done;

                break;

            case LF:

                goto done;

            default:

                state = sw_spaces_before_argument;

                break;

            }

            break;

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                goto invalid;

            }

        }

    }

    s->buffer->pos = p;

    s->state = state;

    return NGX_AGAIN;

done:

    s->buffer->pos = p + 1;

    if (s->arg_start) {

        arg = ngx_array_push(&s->args);

        if (arg == NULL) {

            return NGX_ERROR;

        }

        arg->len = s->arg_end - s->arg_start;

        arg->data = s->arg_start;

        s->arg_start = NULL;

        s->cmd_start = NULL;

        s->quoted = 0;

        s->no_sync_literal = 0;

        s->literal_len = 0;

    }

    s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;

    return NGX_OK;

invalid:

    s->state = sw_start;

    s->quoted = 0;

    s->no_sync_literal = 0;

    s->literal_len = 0;

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

ngx_int_t

‌ngx_mail_smtp_parse_command(ngx_mail_session_t *s)

{

    u_char      ch, *p, *c, c0, c1, c2, c3;

    ngx_str_t  *arg;

    enum {

        sw_start = 0,

        sw_command,

        sw_invalid,

        sw_spaces_before_argument,

        sw_argument,

        sw_almost_done

    } state;

    state = s->state;

    for (p = s->buffer->pos; p < s->buffer->last; p++) {

        ch = *p;

        switch (state) {

        /* SMTP command */

        case sw_start:

            s->cmd_start = p;

            state = sw_command;

            /* fall through */

        case sw_command:

            if (ch == ' ' || ch == CR || ch == LF) {

                c = s->cmd_start;

                if (p - c == 4) {

                    c0 = ngx_toupper(c[0]);

                    c1 = ngx_toupper(c[1]);

                    c2 = ngx_toupper(c[2]);

                    c3 = ngx_toupper(c[3]);

                    if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')

                    {

                        s->command = NGX_SMTP_HELO;

                    } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')

                    {

                        s->command = NGX_SMTP_EHLO;

                    } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')

                    {

                        s->command = NGX_SMTP_QUIT;

                    } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')

                    {

                        s->command = NGX_SMTP_AUTH;

                    } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')

                    {

                        s->command = NGX_SMTP_NOOP;

                    } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')

                    {

                        s->command = NGX_SMTP_MAIL;

                    } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')

                    {

                        s->command = NGX_SMTP_RSET;

                    } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')

                    {

                        s->command = NGX_SMTP_RCPT;

                    } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')

                    {

                        s->command = NGX_SMTP_VRFY;

                    } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')

                    {

                        s->command = NGX_SMTP_EXPN;

                    } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')

                    {

                        s->command = NGX_SMTP_HELP;

                    } else {

                        goto invalid;

                    }

#if (NGX_MAIL_SSL)

                } else if (p - c == 8) {

                    if ((c[0] == 'S'|| c[0] == 's')

                        && (c[1] == 'T'|| c[1] == 't')

                        && (c[2] == 'A'|| c[2] == 'a')

                        && (c[3] == 'R'|| c[3] == 'r')

                        && (c[4] == 'T'|| c[4] == 't')

                        && (c[5] == 'T'|| c[5] == 't')

                        && (c[6] == 'L'|| c[6] == 'l')

                        && (c[7] == 'S'|| c[7] == 's'))

                    {

                        s->command = NGX_SMTP_STARTTLS;

                    } else {

                        goto invalid;

                    }

#endif

                } else {

                    goto invalid;

                }

                s->cmd.data = s->cmd_start;

                s->cmd.len = p - s->cmd_start;

                switch (ch) {

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            }

            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {

                goto invalid;

            }

            break;

        case sw_invalid:

            goto invalid;

        case sw_spaces_before_argument:

            switch (ch) {

            case ' ':

                break;

            case CR:

                state = sw_almost_done;

                s->arg_end = p;

                break;

            case LF:

                s->arg_end = p;

                goto done;

            default:

                if (s->args.nelts <= 10) {

                    state = sw_argument;

                    s->arg_start = p;

                    break;

                }

                goto invalid;

            }

            break;

        case sw_argument:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

                arg = ngx_array_push(&s->args);

                if (arg == NULL) {

                    return NGX_ERROR;

                }

                arg->len = p - s->arg_start;

                arg->data = s->arg_start;

                s->arg_start = NULL;

                switch (ch) {

                case ' ':

                    state = sw_spaces_before_argument;

                    break;

                case CR:

                    state = sw_almost_done;

                    break;

                case LF:

                    goto done;

                }

                break;

            default:

                break;

            }

            break;

        case sw_almost_done:

            switch (ch) {

            case LF:

                goto done;

            default:

                goto invalid;

            }

        }

    }

    s->buffer->pos = p;

    s->state = state;

    return NGX_AGAIN;

done:

    s->buffer->pos = p + 1;

    if (s->arg_start) {

        arg = ngx_array_push(&s->args);

        if (arg == NULL) {

            return NGX_ERROR;

        }

        arg->len = s->arg_end - s->arg_start;

        arg->data = s->arg_start;

        s->arg_start = NULL;

    }

    s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;

    return NGX_OK;

invalid:

    s->state = sw_invalid;

    s->arg_start = NULL;

    /* skip invalid command till LF */

    for (p = s->buffer->pos; p < s->buffer->last; p++) {

        if (*p == LF) {

            s->state = sw_start;

            p++;

            break;

        }

    }

    s->buffer->pos = p;

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

ngx_int_t

‌ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t                 *arg;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    if (s->args.nelts == 0) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    arg = s->args.elts;

    if (arg[0].len == 5) {

        if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {

            if (s->args.nelts == 1) {

                return NGX_MAIL_AUTH_LOGIN;

            }

            if (s->args.nelts == 2) {

                return NGX_MAIL_AUTH_LOGIN_USERNAME;

            }

            return NGX_MAIL_PARSE_INVALID_COMMAND;

        }

        if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {

            if (s->args.nelts == 1) {

                return NGX_MAIL_AUTH_PLAIN;

            }

            if (s->args.nelts == 2) {

                return ngx_mail_auth_plain(s, c, 1);

            }

        }

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    if (arg[0].len == 8) {

        if (s->args.nelts != 1) {

            return NGX_MAIL_PARSE_INVALID_COMMAND;

        }

        if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {

            return NGX_MAIL_AUTH_CRAM_MD5;

        }

    }

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

src/mail/ngx_mail_pop3_module.c - nginx-1.7.9

Global variables defined

ngx_mail_pop3_auth_cram_md5_capability

ngx_mail_pop3_auth_methods

ngx_mail_pop3_auth_plain_capability

ngx_mail_pop3_commands

ngx_mail_pop3_default_capabilities

ngx_mail_pop3_module

ngx_mail_pop3_module_ctx

ngx_mail_pop3_protocol

Functions defined

ngx_mail_pop3_create_srv_conf

ngx_mail_pop3_merge_srv_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_pop3_module.h>

static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf);

static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent,

    void *child);

‌static ngx_str_t  ngx_mail_pop3_default_capabilities[] = {

    ngx_string("TOP"),

    ngx_string("USER"),

    ngx_string("UIDL"),

    ngx_null_string

};

‌static ngx_conf_bitmask_t  ngx_mail_pop3_auth_methods[] = {

    { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },

    { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED },

    { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },

    { ngx_null_string, 0 }

};

‌static ngx_str_t  ngx_mail_pop3_auth_plain_capability =

    ngx_string("+OK methods supported:" CRLF

               "LOGIN" CRLF

               "PLAIN" CRLF

               "." CRLF);

‌static ngx_str_t  ngx_mail_pop3_auth_cram_md5_capability =

    ngx_string("+OK methods supported:" CRLF

               "LOGIN" CRLF

               "PLAIN" CRLF

               "CRAM-MD5" CRLF

               "." CRLF);

‌static ngx_mail_protocol_t  ngx_mail_pop3_protocol = {

    ngx_string("pop3"),

    { 110, 995, 0, 0 },

    NGX_MAIL_POP3_PROTOCOL,

    ngx_mail_pop3_init_session,

    ngx_mail_pop3_init_protocol,

    ngx_mail_pop3_parse_command,

    ngx_mail_pop3_auth_state,

    ngx_string("-ERR internal server error" CRLF)

};

‌static ngx_command_t  ngx_mail_pop3_commands[] = {

    { ngx_string("pop3_capabilities"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_mail_capabilities,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_pop3_srv_conf_t, capabilities),

      NULL },

    { ngx_string("pop3_auth"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_pop3_srv_conf_t, auth_methods),

      &ngx_mail_pop3_auth_methods },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_pop3_module_ctx = {

    &ngx_mail_pop3_protocol,               /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_pop3_create_srv_conf,         /* create server configuration */

    ngx_mail_pop3_merge_srv_conf           /* merge server configuration */

};

‌ngx_module_t  ngx_mail_pop3_module = {

    NGX_MODULE_V1,

    &ngx_mail_pop3_module_ctx,             /* module context */

    ngx_mail_pop3_commands,                /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static void *

‌ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf)

{

    ngx_mail_pop3_srv_conf_t  *pscf;

    pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t));

    if (pscf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))

        != NGX_OK)

    {

        return NULL;

    }

    return pscf;

}

static char *

‌ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_pop3_srv_conf_t *prev = parent;

    ngx_mail_pop3_srv_conf_t *conf = child;

    u_char      *p;

    size_t       size, stls_only_size;

    ngx_str_t   *c, *d;

    ngx_uint_t   i;

    ngx_conf_merge_bitmask_value(conf->auth_methods,

                                 prev->auth_methods,

                                 (NGX_CONF_BITMASK_SET

                                  |NGX_MAIL_AUTH_PLAIN_ENABLED));

    if (conf->capabilities.nelts == 0) {

        conf->capabilities = prev->capabilities;

    }

    if (conf->capabilities.nelts == 0) {

        for (d = ngx_mail_pop3_default_capabilities; d->len; d++) {

            c = ngx_array_push(&conf->capabilities);

            if (c == NULL) {

                return NGX_CONF_ERROR;

            }

            *c = *d;

        }

    }

    size = sizeof("+OK Capability list follows" CRLF) - 1

           + sizeof("." CRLF) - 1;

    stls_only_size = size + sizeof("STLS" CRLF) - 1;

    c = conf->capabilities.elts;

    for (i = 0; i < conf->capabilities.nelts; i++) {

        size += c[i].len + sizeof(CRLF) - 1;

        if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {

            continue;

        }

        stls_only_size += c[i].len + sizeof(CRLF) - 1;

    }

    if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {

        size += sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1;

    } else {

        size += sizeof("SASL LOGIN PLAIN" CRLF) - 1;

    }

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->capability.len = size;

    conf->capability.data = p;

    p = ngx_cpymem(p, "+OK Capability list follows" CRLF,

                   sizeof("+OK Capability list follows" CRLF) - 1);

    for (i = 0; i < conf->capabilities.nelts; i++) {

        p = ngx_cpymem(p, c[i].data, c[i].len);

        *p++ = CR; *p++ = LF;

    }

    if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {

        p = ngx_cpymem(p, "SASL LOGIN PLAIN CRAM-MD5" CRLF,

                       sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1);

    } else {

        p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF,

                       sizeof("SASL LOGIN PLAIN" CRLF) - 1);

    }

    *p++ = '.'; *p++ = CR; *p = LF;

    size += sizeof("STLS" CRLF) - 1;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_capability.len = size;

    conf->starttls_capability.data = p;

    p = ngx_cpymem(p, conf->capability.data,

                   conf->capability.len - (sizeof("." CRLF) - 1));

    p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);

    *p++ = '.'; *p++ = CR; *p = LF;

    if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {

        conf->auth_capability = ngx_mail_pop3_auth_cram_md5_capability;

    } else {

        conf->auth_capability = ngx_mail_pop3_auth_plain_capability;

    }

    p = ngx_pnalloc(cf->pool, stls_only_size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_only_capability.len = stls_only_size;

    conf->starttls_only_capability.data = p;

    p = ngx_cpymem(p, "+OK Capability list follows" CRLF,

                   sizeof("+OK Capability list follows" CRLF) - 1);

    for (i = 0; i < conf->capabilities.nelts; i++) {

        if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {

            continue;

        }

        p = ngx_cpymem(p, c[i].data, c[i].len);

        *p++ = CR; *p++ = LF;

    }

    p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);

    *p++ = '.'; *p++ = CR; *p = LF;

    return NGX_CONF_OK;

}

src/mail/ngx_mail_pop3_handler.c - nginx-1.7.9

Global variables defined

pop3_greeting

pop3_invalid_command

pop3_next

pop3_ok

pop3_password

pop3_username

Functions defined

ngx_mail_pop3_apop

ngx_mail_pop3_auth

ngx_mail_pop3_auth_state

ngx_mail_pop3_capa

ngx_mail_pop3_init_protocol

ngx_mail_pop3_init_session

ngx_mail_pop3_pass

ngx_mail_pop3_stls

ngx_mail_pop3_user

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_pop3_module.h>

static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,

    ngx_int_t stls);

static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);

‌static u_char  pop3_greeting[] = "+OK POP3 ready" CRLF;

‌static u_char  pop3_ok[] = "+OK" CRLF;

‌static u_char  pop3_next[] = "+ " CRLF;

‌static u_char  pop3_username[] = "+ VXNlcm5hbWU6" CRLF;

‌static u_char  pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;

‌static u_char  pop3_invalid_command[] = "-ERR invalid command" CRLF;

void

‌ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)

{

    u_char                    *p;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_mail_pop3_srv_conf_t  *pscf;

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    if (pscf->auth_methods

        & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))

    {

        if (ngx_mail_salt(s, c, cscf) != NGX_OK) {

            ngx_mail_session_internal_server_error(s);

            return;

        }

        s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);

        if (s->out.data == NULL) {

            ngx_mail_session_internal_server_error(s);

            return;

        }

        p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);

        *p++ = ' ';

        p = ngx_cpymem(p, s->salt.data, s->salt.len);

        s->out.len = p - s->out.data;

    } else {

        ngx_str_set(&s->out, pop3_greeting);

    }

    c->read->handler = ngx_mail_pop3_init_protocol;

    ngx_add_timer(c->read, cscf->timeout);

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

        ngx_mail_close_connection(c);

    }

    ngx_mail_send(c->write);

}

void

‌ngx_mail_pop3_init_protocol(ngx_event_t *rev)

{

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    c = rev->data;

    c->log->action = "in auth state";

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    s = c->data;

    if (s->buffer == NULL) {

        if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))

            == NGX_ERROR)

        {

            ngx_mail_session_internal_server_error(s);

            return;

        }

        s->buffer = ngx_create_temp_buf(c->pool, 128);

        if (s->buffer == NULL) {

            ngx_mail_session_internal_server_error(s);

            return;

        }

    }

    s->mail_state = ngx_pop3_start;

    c->read->handler = ngx_mail_pop3_auth_state;

    ngx_mail_pop3_auth_state(rev);

}

void

‌ngx_mail_pop3_auth_state(ngx_event_t *rev)

{

    ngx_int_t            rc;

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    c = rev->data;

    s = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    if (s->out.len) {

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");

        s->blocked = 1;

        return;

    }

    s->blocked = 0;

    rc = ngx_mail_read_command(s, c);

    if (rc == NGX_AGAIN || rc == NGX_ERROR) {

        return;

    }

    ngx_str_set(&s->out, pop3_ok);

    if (rc == NGX_OK) {

        switch (s->mail_state) {

        case ngx_pop3_start:

            switch (s->command) {

            case NGX_POP3_USER:

                rc = ngx_mail_pop3_user(s, c);

                break;

            case NGX_POP3_CAPA:

                rc = ngx_mail_pop3_capa(s, c, 1);

                break;

            case NGX_POP3_APOP:

                rc = ngx_mail_pop3_apop(s, c);

                break;

            case NGX_POP3_AUTH:

                rc = ngx_mail_pop3_auth(s, c);

                break;

            case NGX_POP3_QUIT:

                s->quit = 1;

                break;

            case NGX_POP3_NOOP:

                break;

            case NGX_POP3_STLS:

                rc = ngx_mail_pop3_stls(s, c);

                break;

            default:

                rc = NGX_MAIL_PARSE_INVALID_COMMAND;

                break;

            }

            break;

        case ngx_pop3_user:

            switch (s->command) {

            case NGX_POP3_PASS:

                rc = ngx_mail_pop3_pass(s, c);

                break;

            case NGX_POP3_CAPA:

                rc = ngx_mail_pop3_capa(s, c, 0);

                break;

            case NGX_POP3_QUIT:

                s->quit = 1;

                break;

            case NGX_POP3_NOOP:

                break;

            default:

                rc = NGX_MAIL_PARSE_INVALID_COMMAND;

                break;

            }

            break;

        /* suppress warnings */

        case ngx_pop3_passwd:

            break;

        case ngx_pop3_auth_login_username:

            rc = ngx_mail_auth_login_username(s, c, 0);

            ngx_str_set(&s->out, pop3_password);

            s->mail_state = ngx_pop3_auth_login_password;

            break;

        case ngx_pop3_auth_login_password:

            rc = ngx_mail_auth_login_password(s, c);

            break;

        case ngx_pop3_auth_plain:

            rc = ngx_mail_auth_plain(s, c, 0);

            break;

        case ngx_pop3_auth_cram_md5:

            rc = ngx_mail_auth_cram_md5(s, c);

            break;

        }

    }

    switch (rc) {

    case NGX_DONE:

        ngx_mail_auth(s, c);

        return;

    case NGX_ERROR:

        ngx_mail_session_internal_server_error(s);

        return;

    case NGX_MAIL_PARSE_INVALID_COMMAND:

        s->mail_state = ngx_pop3_start;

        s->state = 0;

        ngx_str_set(&s->out, pop3_invalid_command);

        /* fall through */

    case NGX_OK:

        s->args.nelts = 0;

        s->buffer->pos = s->buffer->start;

        s->buffer->last = s->buffer->start;

        if (s->state) {

            s->arg_start = s->buffer->start;

        }

        ngx_mail_send(c->write);

    }

}

static ngx_int_t

‌ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t  *arg;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    if (s->args.nelts != 1) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    arg = s->args.elts;

    s->login.len = arg[0].len;

    s->login.data = ngx_pnalloc(c->pool, s->login.len);

    if (s->login.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "pop3 login: \"%V\"", &s->login);

    s->mail_state = ngx_pop3_user;

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t  *arg;

    if (s->args.nelts != 1) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    arg = s->args.elts;

    s->passwd.len = arg[0].len;

    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);

    if (s->passwd.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "pop3 passwd: \"%V\"", &s->passwd);

#endif

    return NGX_DONE;

}

static ngx_int_t

‌ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)

{

    ngx_mail_pop3_srv_conf_t  *pscf;

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

#if (NGX_MAIL_SSL)

    if (stls && c->ssl == NULL) {

        ngx_mail_ssl_conf_t  *sslcf;

        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {

            s->out = pscf->starttls_capability;

            return NGX_OK;

        }

        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {

            s->out = pscf->starttls_only_capability;

            return NGX_OK;

        }

    }

#endif

    s->out = pscf->capability;

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)

{

#if (NGX_MAIL_SSL)

    ngx_mail_ssl_conf_t  *sslcf;

    if (c->ssl == NULL) {

        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

        if (sslcf->starttls) {

            c->read->handler = ngx_mail_starttls_handler;

            return NGX_OK;

        }

    }

#endif

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

static ngx_int_t

‌ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t                 *arg;

    ngx_mail_pop3_srv_conf_t  *pscf;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    if (s->args.nelts != 2) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

    if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    arg = s->args.elts;

    s->login.len = arg[0].len;

    s->login.data = ngx_pnalloc(c->pool, s->login.len);

    if (s->login.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    s->passwd.len = arg[1].len;

    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);

    if (s->passwd.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);

    s->auth_method = NGX_MAIL_AUTH_APOP;

    return NGX_DONE;

}

static ngx_int_t

‌ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_int_t                  rc;

    ngx_mail_pop3_srv_conf_t  *pscf;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

    if (s->args.nelts == 0) {

        s->out = pscf->auth_capability;

        s->state = 0;

        return NGX_OK;

    }

    rc = ngx_mail_auth_parse(s, c);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:

        ngx_str_set(&s->out, pop3_username);

        s->mail_state = ngx_pop3_auth_login_username;

        return NGX_OK;

    case NGX_MAIL_AUTH_LOGIN_USERNAME:

        ngx_str_set(&s->out, pop3_password);

        s->mail_state = ngx_pop3_auth_login_password;

        return ngx_mail_auth_login_username(s, c, 1);

    case NGX_MAIL_AUTH_PLAIN:

        ngx_str_set(&s->out, pop3_next);

        s->mail_state = ngx_pop3_auth_plain;

        return NGX_OK;

    case NGX_MAIL_AUTH_CRAM_MD5:

        if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {

            return NGX_MAIL_PARSE_INVALID_COMMAND;

        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {

            s->mail_state = ngx_pop3_auth_cram_md5;

            return NGX_OK;

        }

        return NGX_ERROR;

    }

    return rc;

}

src/mail/ngx_mail_pop3_module.h - nginx-1.7.9

Data types defined

ngx_mail_pop3_srv_conf_t

Macros defined

_NGX_MAIL_POP3_MODULE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_

‌#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_mail.h>

typedef struct {

    ngx_str_t    capability;

    ngx_str_t    starttls_capability;

    ngx_str_t    starttls_only_capability;

    ngx_str_t    auth_capability;

    ngx_uint_t   auth_methods;

    ngx_array_t  capabilities;

‌} ngx_mail_pop3_srv_conf_t;

void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_pop3_init_protocol(ngx_event_t *rev);

void ngx_mail_pop3_auth_state(ngx_event_t *rev);

ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s);

extern ngx_module_t  ngx_mail_pop3_module;

#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */

src/mail/ngx_mail_imap_module.c - nginx-1.7.9

Global variables defined

ngx_mail_imap_auth_methods

ngx_mail_imap_auth_methods_names

ngx_mail_imap_commands

ngx_mail_imap_default_capabilities

ngx_mail_imap_module

ngx_mail_imap_module_ctx

ngx_mail_imap_protocol

Functions defined

ngx_mail_imap_create_srv_conf

ngx_mail_imap_merge_srv_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_imap_module.h>

static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf);

static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent,

    void *child);

‌static ngx_str_t  ngx_mail_imap_default_capabilities[] = {

    ngx_string("IMAP4"),

    ngx_string("IMAP4rev1"),

    ngx_string("UIDPLUS"),

    ngx_null_string

};

‌static ngx_conf_bitmask_t  ngx_mail_imap_auth_methods[] = {

    { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },

    { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },

    { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },

    { ngx_null_string, 0 }

};

‌static ngx_str_t  ngx_mail_imap_auth_methods_names[] = {

    ngx_string("AUTH=PLAIN"),

    ngx_string("AUTH=LOGIN"),

    ngx_null_string,  /* APOP */

    ngx_string("AUTH=CRAM-MD5"),

    ngx_null_string   /* NONE */

};

‌static ngx_mail_protocol_t  ngx_mail_imap_protocol = {

    ngx_string("imap"),

    { 143, 993, 0, 0 },

    NGX_MAIL_IMAP_PROTOCOL,

    ngx_mail_imap_init_session,

    ngx_mail_imap_init_protocol,

    ngx_mail_imap_parse_command,

    ngx_mail_imap_auth_state,

    ngx_string("* BAD internal server error" CRLF)

};

‌static ngx_command_t  ngx_mail_imap_commands[] = {

    { ngx_string("imap_client_buffer"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size),

      NULL },

    { ngx_string("imap_capabilities"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_mail_capabilities,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_imap_srv_conf_t, capabilities),

      NULL },

    { ngx_string("imap_auth"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_imap_srv_conf_t, auth_methods),

      &ngx_mail_imap_auth_methods },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_imap_module_ctx = {

    &ngx_mail_imap_protocol,               /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_imap_create_srv_conf,         /* create server configuration */

    ngx_mail_imap_merge_srv_conf           /* merge server configuration */

};

‌ngx_module_t  ngx_mail_imap_module = {

    NGX_MODULE_V1,

    &ngx_mail_imap_module_ctx,             /* module context */

    ngx_mail_imap_commands,                /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static void *

‌ngx_mail_imap_create_srv_conf(ngx_conf_t *cf)

{

    ngx_mail_imap_srv_conf_t  *iscf;

    iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t));

    if (iscf == NULL) {

        return NULL;

    }

    iscf->client_buffer_size = NGX_CONF_UNSET_SIZE;

    if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))

        != NGX_OK)

    {

        return NULL;

    }

    return iscf;

}

static char *

‌ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_imap_srv_conf_t *prev = parent;

    ngx_mail_imap_srv_conf_t *conf = child;

    u_char      *p, *auth;

    size_t       size;

    ngx_str_t   *c, *d;

    ngx_uint_t   i, m;

    ngx_conf_merge_size_value(conf->client_buffer_size,

                              prev->client_buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_bitmask_value(conf->auth_methods,

                              prev->auth_methods,

                              (NGX_CONF_BITMASK_SET

                               |NGX_MAIL_AUTH_PLAIN_ENABLED));

    if (conf->capabilities.nelts == 0) {

        conf->capabilities = prev->capabilities;

    }

    if (conf->capabilities.nelts == 0) {

        for (d = ngx_mail_imap_default_capabilities; d->len; d++) {

            c = ngx_array_push(&conf->capabilities);

            if (c == NULL) {

                return NGX_CONF_ERROR;

            }

            *c = *d;

        }

    }

    size = sizeof("* CAPABILITY" CRLF) - 1;

    c = conf->capabilities.elts;

    for (i = 0; i < conf->capabilities.nelts; i++) {

        size += 1 + c[i].len;

    }

    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;

         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;

         m <<= 1, i++)

    {

        if (m & conf->auth_methods) {

            size += 1 + ngx_mail_imap_auth_methods_names[i].len;

        }

    }

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->capability.len = size;

    conf->capability.data = p;

    p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1);

    for (i = 0; i < conf->capabilities.nelts; i++) {

        *p++ = ' ';

        p = ngx_cpymem(p, c[i].data, c[i].len);

    }

    auth = p;

    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;

         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;

         m <<= 1, i++)

    {

        if (m & conf->auth_methods) {

            *p++ = ' ';

            p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data,

                           ngx_mail_imap_auth_methods_names[i].len);

        }

    }

    *p++ = CR; *p = LF;

    size += sizeof(" STARTTLS") - 1;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_capability.len = size;

    conf->starttls_capability.data = p;

    p = ngx_cpymem(p, conf->capability.data,

                   conf->capability.len - (sizeof(CRLF) - 1));

    p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1);

    *p++ = CR; *p = LF;

    size = (auth - conf->capability.data) + sizeof(CRLF) - 1

            + sizeof(" STARTTLS LOGINDISABLED") - 1;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_only_capability.len = size;

    conf->starttls_only_capability.data = p;

    p = ngx_cpymem(p, conf->capability.data,

                   auth - conf->capability.data);

    p = ngx_cpymem(p, " STARTTLS LOGINDISABLED",

                   sizeof(" STARTTLS LOGINDISABLED") - 1);

    *p++ = CR; *p = LF;

    return NGX_CONF_OK;

}

src/mail/ngx_mail_imap_handler.c - nginx-1.7.9

Global variables defined

imap_bye

imap_greeting

imap_invalid_command

imap_next

imap_ok

imap_password

imap_plain_next

imap_star

imap_username

Functions defined

ngx_mail_imap_auth_state

ngx_mail_imap_authenticate

ngx_mail_imap_capability

ngx_mail_imap_init_protocol

ngx_mail_imap_init_session

ngx_mail_imap_login

ngx_mail_imap_starttls

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_imap_module.h>

static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s,

    ngx_connection_t *c);

static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s,

    ngx_connection_t *c);

static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s,

    ngx_connection_t *c);

static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,

    ngx_connection_t *c);

‌static u_char  imap_greeting[] = "* OK IMAP4 ready" CRLF;

‌static u_char  imap_star[] = "* ";

‌static u_char  imap_ok[] = "OK completed" CRLF;

‌static u_char  imap_next[] = "+ OK" CRLF;

‌static u_char  imap_plain_next[] = "+ " CRLF;

‌static u_char  imap_username[] = "+ VXNlcm5hbWU6" CRLF;

‌static u_char  imap_password[] = "+ UGFzc3dvcmQ6" CRLF;

‌static u_char  imap_bye[] = "* BYE" CRLF;

‌static u_char  imap_invalid_command[] = "BAD invalid command" CRLF;

void

‌ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_mail_core_srv_conf_t  *cscf;

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    ngx_str_set(&s->out, imap_greeting);

    c->read->handler = ngx_mail_imap_init_protocol;

    ngx_add_timer(c->read, cscf->timeout);

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

        ngx_mail_close_connection(c);

    }

    ngx_mail_send(c->write);

}

void

‌ngx_mail_imap_init_protocol(ngx_event_t *rev)

{

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_imap_srv_conf_t  *iscf;

    c = rev->data;

    c->log->action = "in auth state";

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    s = c->data;

    if (s->buffer == NULL) {

        if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))

            == NGX_ERROR)

        {

            ngx_mail_session_internal_server_error(s);

            return;

        }

        iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);

        s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size);

        if (s->buffer == NULL) {

            ngx_mail_session_internal_server_error(s);

            return;

        }

    }

    s->mail_state = ngx_imap_start;

    c->read->handler = ngx_mail_imap_auth_state;

    ngx_mail_imap_auth_state(rev);

}

void

‌ngx_mail_imap_auth_state(ngx_event_t *rev)

{

    u_char              *p, *dst, *src, *end;

    ngx_str_t           *arg;

    ngx_int_t            rc;

    ngx_uint_t           tag, i;

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    c = rev->data;

    s = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    if (s->out.len) {

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy");

        s->blocked = 1;

        return;

    }

    s->blocked = 0;

    rc = ngx_mail_read_command(s, c);

    if (rc == NGX_AGAIN || rc == NGX_ERROR) {

        return;

    }

    tag = 1;

    s->text.len = 0;

    ngx_str_set(&s->out, imap_ok);

    if (rc == NGX_OK) {

        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i",

                       s->command);

        if (s->backslash) {

            arg = s->args.elts;

            for (i = 0; i < s->args.nelts; i++) {

                dst = arg[i].data;

                end = dst + arg[i].len;

                for (src = dst; src < end; dst++) {

                    *dst = *src;

                    if (*src++ == '\\') {

                        *dst = *src++;

                    }

                }

                arg[i].len = dst - arg[i].data;

            }

            s->backslash = 0;

        }

        switch (s->mail_state) {

        case ngx_imap_start:

            switch (s->command) {

            case NGX_IMAP_LOGIN:

                rc = ngx_mail_imap_login(s, c);

                break;

            case NGX_IMAP_AUTHENTICATE:

                rc = ngx_mail_imap_authenticate(s, c);

                tag = (rc != NGX_OK);

                break;

            case NGX_IMAP_CAPABILITY:

                rc = ngx_mail_imap_capability(s, c);

                break;

            case NGX_IMAP_LOGOUT:

                s->quit = 1;

                ngx_str_set(&s->text, imap_bye);

                break;

            case NGX_IMAP_NOOP:

                break;

            case NGX_IMAP_STARTTLS:

                rc = ngx_mail_imap_starttls(s, c);

                break;

            default:

                rc = NGX_MAIL_PARSE_INVALID_COMMAND;

                break;

            }

            break;

        case ngx_imap_auth_login_username:

            rc = ngx_mail_auth_login_username(s, c, 0);

            tag = 0;

            ngx_str_set(&s->out, imap_password);

            s->mail_state = ngx_imap_auth_login_password;

            break;

        case ngx_imap_auth_login_password:

            rc = ngx_mail_auth_login_password(s, c);

            break;

        case ngx_imap_auth_plain:

            rc = ngx_mail_auth_plain(s, c, 0);

            break;

        case ngx_imap_auth_cram_md5:

            rc = ngx_mail_auth_cram_md5(s, c);

            break;

        }

    } else if (rc == NGX_IMAP_NEXT) {

        tag = 0;

        ngx_str_set(&s->out, imap_next);

    }

    switch (rc) {

    case NGX_DONE:

        ngx_mail_auth(s, c);

        return;

    case NGX_ERROR:

        ngx_mail_session_internal_server_error(s);

        return;

    case NGX_MAIL_PARSE_INVALID_COMMAND:

        s->state = 0;

        ngx_str_set(&s->out, imap_invalid_command);

        s->mail_state = ngx_imap_start;

        break;

    }

    if (tag) {

        if (s->tag.len == 0) {

            ngx_str_set(&s->tag, imap_star);

        }

        if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) {

            s->tagged_line.len = s->tag.len + s->text.len + s->out.len;

            s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len);

            if (s->tagged_line.data == NULL) {

                ngx_mail_close_connection(c);

                return;

            }

        }

        p = s->tagged_line.data;

        if (s->text.len) {

            p = ngx_cpymem(p, s->text.data, s->text.len);

        }

        p = ngx_cpymem(p, s->tag.data, s->tag.len);

        ngx_memcpy(p, s->out.data, s->out.len);

        s->out.len = s->text.len + s->tag.len + s->out.len;

        s->out.data = s->tagged_line.data;

    }

    if (rc != NGX_IMAP_NEXT) {

        s->args.nelts = 0;

        if (s->state) {

            /* preserve tag */

            s->arg_start = s->buffer->start + s->tag.len;

            s->buffer->pos = s->arg_start;

            s->buffer->last = s->arg_start;

        } else {

            s->buffer->pos = s->buffer->start;

            s->buffer->last = s->buffer->start;

            s->tag.len = 0;

        }

    }

    ngx_mail_send(c->write);

}

static ngx_int_t

‌ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t  *arg;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    arg = s->args.elts;

    if (s->args.nelts != 2 || arg[0].len == 0) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

    s->login.len = arg[0].len;

    s->login.data = ngx_pnalloc(c->pool, s->login.len);

    if (s->login.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    s->passwd.len = arg[1].len;

    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);

    if (s->passwd.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

#if (NGX_DEBUG_MAIL_PASSWD)

    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "imap login:\"%V\" passwd:\"%V\"",

                   &s->login, &s->passwd);

#else

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "imap login:\"%V\"", &s->login);

#endif

    return NGX_DONE;

}

static ngx_int_t

‌ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_int_t                  rc;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_mail_imap_srv_conf_t  *iscf;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    rc = ngx_mail_auth_parse(s, c);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:

        ngx_str_set(&s->out, imap_username);

        s->mail_state = ngx_imap_auth_login_username;

        return NGX_OK;

    case NGX_MAIL_AUTH_LOGIN_USERNAME:

        ngx_str_set(&s->out, imap_password);

        s->mail_state = ngx_imap_auth_login_password;

        return ngx_mail_auth_login_username(s, c, 1);

    case NGX_MAIL_AUTH_PLAIN:

        ngx_str_set(&s->out, imap_plain_next);

        s->mail_state = ngx_imap_auth_plain;

        return NGX_OK;

    case NGX_MAIL_AUTH_CRAM_MD5:

        iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);

        if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {

            return NGX_MAIL_PARSE_INVALID_COMMAND;

        }

        if (s->salt.data == NULL) {

            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {

                return NGX_ERROR;

            }

        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {

            s->mail_state = ngx_imap_auth_cram_md5;

            return NGX_OK;

        }

        return NGX_ERROR;

    }

    return rc;

}

static ngx_int_t

‌ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_mail_imap_srv_conf_t  *iscf;

    iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);

#if (NGX_MAIL_SSL)

    if (c->ssl == NULL) {

        ngx_mail_ssl_conf_t  *sslcf;

        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {

            s->text = iscf->starttls_capability;

            return NGX_OK;

        }

        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {

            s->text = iscf->starttls_only_capability;

            return NGX_OK;

        }

    }

#endif

    s->text = iscf->capability;

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)

{

#if (NGX_MAIL_SSL)

    ngx_mail_ssl_conf_t  *sslcf;

    if (c->ssl == NULL) {

        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

        if (sslcf->starttls) {

            c->read->handler = ngx_mail_starttls_handler;

            return NGX_OK;

        }

    }

#endif

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

src/mail/ngx_mail_imap_module.h - nginx-1.7.9

Data types defined

ngx_mail_imap_srv_conf_t

Macros defined

_NGX_MAIL_IMAP_MODULE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_

‌#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_mail.h>

typedef struct {

    size_t       client_buffer_size;

    ngx_str_t    capability;

    ngx_str_t    starttls_capability;

    ngx_str_t    starttls_only_capability;

    ngx_uint_t   auth_methods;

    ngx_array_t  capabilities;

‌} ngx_mail_imap_srv_conf_t;

void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_imap_init_protocol(ngx_event_t *rev);

void ngx_mail_imap_auth_state(ngx_event_t *rev);

ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s);

extern ngx_module_t  ngx_mail_imap_module;

#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */

src/mail/ngx_mail_smtp_module.c - nginx-1.7.9

Global variables defined

ngx_mail_smtp_auth_methods

ngx_mail_smtp_auth_methods_names

ngx_mail_smtp_commands

ngx_mail_smtp_module

ngx_mail_smtp_module_ctx

ngx_mail_smtp_protocol

Functions defined

ngx_mail_smtp_create_srv_conf

ngx_mail_smtp_merge_srv_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_smtp_module.h>

static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf);

static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent,

    void *child);

‌static ngx_conf_bitmask_t  ngx_mail_smtp_auth_methods[] = {

    { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },

    { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },

    { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },

    { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },

    { ngx_null_string, 0 }

};

‌static ngx_str_t  ngx_mail_smtp_auth_methods_names[] = {

    ngx_string("PLAIN"),

    ngx_string("LOGIN"),

    ngx_null_string,  /* APOP */

    ngx_string("CRAM-MD5"),

    ngx_null_string   /* NONE */

};

‌static ngx_mail_protocol_t  ngx_mail_smtp_protocol = {

    ngx_string("smtp"),

    { 25, 465, 587, 0 },

    NGX_MAIL_SMTP_PROTOCOL,

    ngx_mail_smtp_init_session,

    ngx_mail_smtp_init_protocol,

    ngx_mail_smtp_parse_command,

    ngx_mail_smtp_auth_state,

    ngx_string("451 4.3.2 Internal server error" CRLF)

};

‌static ngx_command_t  ngx_mail_smtp_commands[] = {

    { ngx_string("smtp_client_buffer"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size),

      NULL },

    { ngx_string("smtp_greeting_delay"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay),

      NULL },

    { ngx_string("smtp_capabilities"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_mail_capabilities,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_smtp_srv_conf_t, capabilities),

      NULL },

    { ngx_string("smtp_auth"),

      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_MAIL_SRV_CONF_OFFSET,

      offsetof(ngx_mail_smtp_srv_conf_t, auth_methods),

      &ngx_mail_smtp_auth_methods },

      ngx_null_command

};

‌static ngx_mail_module_t  ngx_mail_smtp_module_ctx = {

    &ngx_mail_smtp_protocol,               /* protocol */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_mail_smtp_create_srv_conf,         /* create server configuration */

    ngx_mail_smtp_merge_srv_conf           /* merge server configuration */

};

‌ngx_module_t  ngx_mail_smtp_module = {

    NGX_MODULE_V1,

    &ngx_mail_smtp_module_ctx,             /* module context */

    ngx_mail_smtp_commands,                /* module directives */

    NGX_MAIL_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static void *

‌ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf)

{

    ngx_mail_smtp_srv_conf_t  *sscf;

    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t));

    if (sscf == NULL) {

        return NULL;

    }

    sscf->client_buffer_size = NGX_CONF_UNSET_SIZE;

    sscf->greeting_delay = NGX_CONF_UNSET_MSEC;

    if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))

        != NGX_OK)

    {

        return NULL;

    }

    return sscf;

}

static char *

‌ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_mail_smtp_srv_conf_t *prev = parent;

    ngx_mail_smtp_srv_conf_t *conf = child;

    u_char                    *p, *auth, *last;

    size_t                     size;

    ngx_str_t                 *c;

    ngx_uint_t                 i, m, auth_enabled;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_conf_merge_size_value(conf->client_buffer_size,

                              prev->client_buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_msec_value(conf->greeting_delay,

                              prev->greeting_delay, 0);

    ngx_conf_merge_bitmask_value(conf->auth_methods,

                              prev->auth_methods,

                              (NGX_CONF_BITMASK_SET

                               |NGX_MAIL_AUTH_PLAIN_ENABLED

                               |NGX_MAIL_AUTH_LOGIN_ENABLED));

    cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);

    size = sizeof("220  ESMTP ready" CRLF) - 1 + cscf->server_name.len;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->greeting.len = size;

    conf->greeting.data = p;

    *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' ';

    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);

    ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1);

    size = sizeof("250 " CRLF) - 1 + cscf->server_name.len;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->server_name.len = size;

    conf->server_name.data = p;

    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';

    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);

    *p++ = CR; *p = LF;

    if (conf->capabilities.nelts == 0) {

        conf->capabilities = prev->capabilities;

    }

    size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;

    c = conf->capabilities.elts;

    for (i = 0; i < conf->capabilities.nelts; i++) {

        size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;

    }

    auth_enabled = 0;

    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;

         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;

         m <<= 1, i++)

    {

        if (m & conf->auth_methods) {

            size += 1 + ngx_mail_smtp_auth_methods_names[i].len;

            auth_enabled = 1;

        }

    }

    if (auth_enabled) {

        size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;

    }

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->capability.len = size;

    conf->capability.data = p;

    last = p;

    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';

    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);

    *p++ = CR; *p++ = LF;

    for (i = 0; i < conf->capabilities.nelts; i++) {

        last = p;

        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';

        p = ngx_cpymem(p, c[i].data, c[i].len);

        *p++ = CR; *p++ = LF;

    }

    auth = p;

    if (auth_enabled) {

        last = p;

        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';

        *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';

        for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;

             m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;

             m <<= 1, i++)

        {

            if (m & conf->auth_methods) {

                *p++ = ' ';

                p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,

                               ngx_mail_smtp_auth_methods_names[i].len);

            }

        }

        *p++ = CR; *p = LF;

    } else {

        last[3] = ' ';

    }

    size += sizeof("250 STARTTLS" CRLF) - 1;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_capability.len = size;

    conf->starttls_capability.data = p;

    p = ngx_cpymem(p, conf->capability.data, conf->capability.len);

    p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);

    p = conf->starttls_capability.data

        + (last - conf->capability.data) + 3;

    *p = '-';

    size = (auth - conf->capability.data)

            + sizeof("250 STARTTLS" CRLF) - 1;

    p = ngx_pnalloc(cf->pool, size);

    if (p == NULL) {

        return NGX_CONF_ERROR;

    }

    conf->starttls_only_capability.len = size;

    conf->starttls_only_capability.data = p;

    p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);

    ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);

    if (last < auth) {

        p = conf->starttls_only_capability.data

            + (last - conf->capability.data) + 3;

        *p = '-';

    }

    return NGX_CONF_OK;

}

src/mail/ngx_mail_smtp_handler.c - nginx-1.7.9

Global variables defined

smtp_auth_required

smtp_bad_sequence

smtp_bye

smtp_invalid_argument

smtp_invalid_command

smtp_invalid_pipelining

smtp_next

smtp_ok

smtp_password

smtp_starttls

smtp_tempunavail

smtp_unavailable

smtp_username

Functions defined

ngx_mail_smtp_auth

ngx_mail_smtp_auth_state

ngx_mail_smtp_create_buffer

ngx_mail_smtp_discard_command

ngx_mail_smtp_greeting

ngx_mail_smtp_helo

ngx_mail_smtp_init_protocol

ngx_mail_smtp_init_session

ngx_mail_smtp_invalid_pipelining

ngx_mail_smtp_log_rejected_command

ngx_mail_smtp_mail

ngx_mail_smtp_rcpt

ngx_mail_smtp_resolve_addr_handler

ngx_mail_smtp_resolve_name

ngx_mail_smtp_resolve_name_handler

ngx_mail_smtp_rset

ngx_mail_smtp_starttls

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_mail.h>

#include <ngx_mail_smtp_module.h>

static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);

static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);

static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);

static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);

static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);

static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,

    ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,

    ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);

static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,

    ngx_connection_t *c, char *err);

static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,

    ngx_connection_t *c, char *err);

‌static u_char  smtp_ok[] = "250 2.0.0 OK" CRLF;

‌static u_char  smtp_bye[] = "221 2.0.0 Bye" CRLF;

‌static u_char  smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;

‌static u_char  smtp_next[] = "334 " CRLF;

‌static u_char  smtp_username[] = "334 VXNlcm5hbWU6" CRLF;

‌static u_char  smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;

‌static u_char  smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF;

‌static u_char  smtp_invalid_pipelining[] =

   "503 5.5.0 Improper use of SMTP command pipelining" CRLF;

‌static u_char  smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;

‌static u_char  smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;

‌static u_char  smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;

‌static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");

‌static ngx_str_t  smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");

void

‌ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_resolver_ctx_t        *ctx;

    ngx_mail_core_srv_conf_t  *cscf;

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    if (cscf->resolver == NULL) {

        s->host = smtp_unavailable;

        ngx_mail_smtp_greeting(s, c);

        return;

    }

#if (NGX_HAVE_UNIX_DOMAIN)

    if (c->sockaddr->sa_family == AF_UNIX) {

        s->host = smtp_tempunavail;

        ngx_mail_smtp_greeting(s, c);

        return;

    }

#endif

    c->log->action = "in resolving client address";

    ctx = ngx_resolve_start(cscf->resolver, NULL);

    if (ctx == NULL) {

        ngx_mail_close_connection(c);

        return;

    }

    ctx->addr.sockaddr = c->sockaddr;

    ctx->addr.socklen = c->socklen;

    ctx->handler = ngx_mail_smtp_resolve_addr_handler;

    ctx->data = s;

    ctx->timeout = cscf->resolver_timeout;

    if (ngx_resolve_addr(ctx) != NGX_OK) {

        ngx_mail_close_connection(c);

    }

}

static void

‌ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)

{

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    s = ctx->data;

    c = s->connection;

    if (ctx->state) {

        ngx_log_error(NGX_LOG_ERR, c->log, 0,

                      "%V could not be resolved (%i: %s)",

                      &c->addr_text, ctx->state,

                      ngx_resolver_strerror(ctx->state));

        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {

            s->host = smtp_unavailable;

        } else {

            s->host = smtp_tempunavail;

        }

        ngx_resolve_addr_done(ctx);

        ngx_mail_smtp_greeting(s, s->connection);

        return;

    }

    c->log->action = "in resolving client hostname";

    s->host.data = ngx_pstrdup(c->pool, &ctx->name);

    if (s->host.data == NULL) {

        ngx_resolve_addr_done(ctx);

        ngx_mail_close_connection(c);

        return;

    }

    s->host.len = ctx->name.len;

    ngx_resolve_addr_done(ctx);

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "address resolved: %V", &s->host);

    c->read->handler = ngx_mail_smtp_resolve_name;

    ngx_post_event(c->read, &ngx_posted_events);

}

static void

‌ngx_mail_smtp_resolve_name(ngx_event_t *rev)

{

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_resolver_ctx_t        *ctx;

    ngx_mail_core_srv_conf_t  *cscf;

    c = rev->data;

    s = c->data;

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    ctx = ngx_resolve_start(cscf->resolver, NULL);

    if (ctx == NULL) {

        ngx_mail_close_connection(c);

        return;

    }

    ctx->name = s->host;

    ctx->handler = ngx_mail_smtp_resolve_name_handler;

    ctx->data = s;

    ctx->timeout = cscf->resolver_timeout;

    if (ngx_resolve_name(ctx) != NGX_OK) {

        ngx_mail_close_connection(c);

    }

}

static void

‌ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)

{

    ngx_uint_t           i;

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    s = ctx->data;

    c = s->connection;

    if (ctx->state) {

        ngx_log_error(NGX_LOG_ERR, c->log, 0,

                      "\"%V\" could not be resolved (%i: %s)",

                      &ctx->name, ctx->state,

                      ngx_resolver_strerror(ctx->state));

        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {

            s->host = smtp_unavailable;

        } else {

            s->host = smtp_tempunavail;

        }

    } else {

#if (NGX_DEBUG)

        {

        u_char     text[NGX_SOCKADDR_STRLEN];

        ngx_str_t  addr;

        addr.data = text;

        for (i = 0; i < ctx->naddrs; i++) {

            addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,

                                     ctx->addrs[i].socklen,

                                     text, NGX_SOCKADDR_STRLEN, 0);

            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                           "name was resolved to %V", &addr);

        }

        }

#endif

        for (i = 0; i < ctx->naddrs; i++) {

            if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,

                                 c->sockaddr, c->socklen, 0)

                == NGX_OK)

            {

                goto found;

            }

        }

        s->host = smtp_unavailable;

    }

found:

    ngx_resolve_name_done(ctx);

    ngx_mail_smtp_greeting(s, c);

}

static void

‌ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_msec_t                 timeout;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_mail_smtp_srv_conf_t  *sscf;

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "smtp greeting for \"%V\"", &s->host);

    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

    timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;

    ngx_add_timer(c->read, timeout);

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

        ngx_mail_close_connection(c);

    }

    if (sscf->greeting_delay) {

         c->read->handler = ngx_mail_smtp_invalid_pipelining;

         return;

    }

    c->read->handler = ngx_mail_smtp_init_protocol;

    s->out = sscf->greeting;

    ngx_mail_send(c->write);

}

static void

‌ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)

{

    ngx_connection_t          *c;

    ngx_mail_session_t        *s;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_mail_smtp_srv_conf_t  *sscf;

    c = rev->data;

    s = c->data;

    c->log->action = "in delay pipelining state";

    if (rev->timedout) {

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting");

        rev->timedout = 0;

        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

        c->read->handler = ngx_mail_smtp_init_protocol;

        ngx_add_timer(c->read, cscf->timeout);

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            ngx_mail_close_connection(c);

            return;

        }

        sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

        s->out = sscf->greeting;

    } else {

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining");

        if (s->buffer == NULL) {

            if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {

                return;

            }

        }

        if (ngx_mail_smtp_discard_command(s, c,

                                "client was rejected before greeting: \"%V\"")

            != NGX_OK)

        {

            return;

        }

        ngx_str_set(&s->out, smtp_invalid_pipelining);

        s->quit = 1;

    }

    ngx_mail_send(c->write);

}

void

‌ngx_mail_smtp_init_protocol(ngx_event_t *rev)

{

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    c = rev->data;

    c->log->action = "in auth state";

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    s = c->data;

    if (s->buffer == NULL) {

        if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {

            return;

        }

    }

    s->mail_state = ngx_smtp_start;

    c->read->handler = ngx_mail_smtp_auth_state;

    ngx_mail_smtp_auth_state(rev);

}

static ngx_int_t

‌ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_mail_smtp_srv_conf_t  *sscf;

    if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {

        ngx_mail_session_internal_server_error(s);

        return NGX_ERROR;

    }

    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

    s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);

    if (s->buffer == NULL) {

        ngx_mail_session_internal_server_error(s);

        return NGX_ERROR;

    }

    return NGX_OK;

}

void

‌ngx_mail_smtp_auth_state(ngx_event_t *rev)

{

    ngx_int_t            rc;

    ngx_connection_t    *c;

    ngx_mail_session_t  *s;

    c = rev->data;

    s = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state");

    if (rev->timedout) {

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        c->timedout = 1;

        ngx_mail_close_connection(c);

        return;

    }

    if (s->out.len) {

        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");

        s->blocked = 1;

        return;

    }

    s->blocked = 0;

    rc = ngx_mail_read_command(s, c);

    if (rc == NGX_AGAIN || rc == NGX_ERROR) {

        return;

    }

    ngx_str_set(&s->out, smtp_ok);

    if (rc == NGX_OK) {

        switch (s->mail_state) {

        case ngx_smtp_start:

            switch (s->command) {

            case NGX_SMTP_HELO:

            case NGX_SMTP_EHLO:

                rc = ngx_mail_smtp_helo(s, c);

                break;

            case NGX_SMTP_AUTH:

                rc = ngx_mail_smtp_auth(s, c);

                break;

            case NGX_SMTP_QUIT:

                s->quit = 1;

                ngx_str_set(&s->out, smtp_bye);

                break;

            case NGX_SMTP_MAIL:

                rc = ngx_mail_smtp_mail(s, c);

                break;

            case NGX_SMTP_RCPT:

                rc = ngx_mail_smtp_rcpt(s, c);

                break;

            case NGX_SMTP_RSET:

                rc = ngx_mail_smtp_rset(s, c);

                break;

            case NGX_SMTP_NOOP:

                break;

            case NGX_SMTP_STARTTLS:

                rc = ngx_mail_smtp_starttls(s, c);

                ngx_str_set(&s->out, smtp_starttls);

                break;

            default:

                rc = NGX_MAIL_PARSE_INVALID_COMMAND;

                break;

            }

            break;

        case ngx_smtp_auth_login_username:

            rc = ngx_mail_auth_login_username(s, c, 0);

            ngx_str_set(&s->out, smtp_password);

            s->mail_state = ngx_smtp_auth_login_password;

            break;

        case ngx_smtp_auth_login_password:

            rc = ngx_mail_auth_login_password(s, c);

            break;

        case ngx_smtp_auth_plain:

            rc = ngx_mail_auth_plain(s, c, 0);

            break;

        case ngx_smtp_auth_cram_md5:

            rc = ngx_mail_auth_cram_md5(s, c);

            break;

        }

    }

    if (s->buffer->pos < s->buffer->last) {

        s->blocked = 1;

    }

    switch (rc) {

    case NGX_DONE:

        ngx_mail_auth(s, c);

        return;

    case NGX_ERROR:

        ngx_mail_session_internal_server_error(s);

        return;

    case NGX_MAIL_PARSE_INVALID_COMMAND:

        s->mail_state = ngx_smtp_start;

        s->state = 0;

        ngx_str_set(&s->out, smtp_invalid_command);

        /* fall through */

    case NGX_OK:

        s->args.nelts = 0;

        if (s->buffer->pos == s->buffer->last) {

            s->buffer->pos = s->buffer->start;

            s->buffer->last = s->buffer->start;

        }

        if (s->state) {

            s->arg_start = s->buffer->pos;

        }

        ngx_mail_send(c->write);

    }

}

static ngx_int_t

‌ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t                 *arg;

    ngx_mail_smtp_srv_conf_t  *sscf;

    if (s->args.nelts != 1) {

        ngx_str_set(&s->out, smtp_invalid_argument);

        s->state = 0;

        return NGX_OK;

    }

    arg = s->args.elts;

    s->smtp_helo.len = arg[0].len;

    s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);

    if (s->smtp_helo.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);

    ngx_str_null(&s->smtp_from);

    ngx_str_null(&s->smtp_to);

    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

    if (s->command == NGX_SMTP_HELO) {

        s->out = sscf->server_name;

    } else {

        s->esmtp = 1;

#if (NGX_MAIL_SSL)

        if (c->ssl == NULL) {

            ngx_mail_ssl_conf_t  *sslcf;

            sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

            if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {

                s->out = sscf->starttls_capability;

                return NGX_OK;

            }

            if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {

                s->out = sscf->starttls_only_capability;

                return NGX_OK;

            }

        }

#endif

        s->out = sscf->capability;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_int_t                  rc;

    ngx_mail_core_srv_conf_t  *cscf;

    ngx_mail_smtp_srv_conf_t  *sscf;

#if (NGX_MAIL_SSL)

    if (ngx_mail_starttls_only(s, c)) {

        return NGX_MAIL_PARSE_INVALID_COMMAND;

    }

#endif

    if (s->args.nelts == 0) {

        ngx_str_set(&s->out, smtp_invalid_argument);

        s->state = 0;

        return NGX_OK;

    }

    rc = ngx_mail_auth_parse(s, c);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:

        ngx_str_set(&s->out, smtp_username);

        s->mail_state = ngx_smtp_auth_login_username;

        return NGX_OK;

    case NGX_MAIL_AUTH_LOGIN_USERNAME:

        ngx_str_set(&s->out, smtp_password);

        s->mail_state = ngx_smtp_auth_login_password;

        return ngx_mail_auth_login_username(s, c, 1);

    case NGX_MAIL_AUTH_PLAIN:

        ngx_str_set(&s->out, smtp_next);

        s->mail_state = ngx_smtp_auth_plain;

        return NGX_OK;

    case NGX_MAIL_AUTH_CRAM_MD5:

        sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

        if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {

            return NGX_MAIL_PARSE_INVALID_COMMAND;

        }

        if (s->salt.data == NULL) {

            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {

                return NGX_ERROR;

            }

        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {

            s->mail_state = ngx_smtp_auth_cram_md5;

            return NGX_OK;

        }

        return NGX_ERROR;

    }

    return rc;

}

static ngx_int_t

‌ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t                 *arg, cmd;

    ngx_mail_smtp_srv_conf_t  *sscf;

    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);

    if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {

        ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");

        ngx_str_set(&s->out, smtp_auth_required);

        return NGX_OK;

    }

    /* auth none */

    if (s->smtp_from.len) {

        ngx_str_set(&s->out, smtp_bad_sequence);

        return NGX_OK;

    }

    if (s->args.nelts == 0) {

        ngx_str_set(&s->out, smtp_invalid_argument);

        return NGX_OK;

    }

    arg = s->args.elts;

    arg += s->args.nelts - 1;

    cmd.len = arg->data + arg->len - s->cmd.data;

    cmd.data = s->cmd.data;

    s->smtp_from.len = cmd.len;

    s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);

    if (s->smtp_from.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "smtp mail from:\"%V\"", &s->smtp_from);

    ngx_str_set(&s->out, smtp_ok);

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_t  *arg, cmd;

    if (s->smtp_from.len == 0) {

        ngx_str_set(&s->out, smtp_bad_sequence);

        return NGX_OK;

    }

    if (s->args.nelts == 0) {

        ngx_str_set(&s->out, smtp_invalid_argument);

        return NGX_OK;

    }

    arg = s->args.elts;

    arg += s->args.nelts - 1;

    cmd.len = arg->data + arg->len - s->cmd.data;

    cmd.data = s->cmd.data;

    s->smtp_to.len = cmd.len;

    s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);

    if (s->smtp_to.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,

                   "smtp rcpt to:\"%V\"", &s->smtp_to);

    s->auth_method = NGX_MAIL_AUTH_NONE;

    return NGX_DONE;

}

static ngx_int_t

‌ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)

{

    ngx_str_null(&s->smtp_from);

    ngx_str_null(&s->smtp_to);

    ngx_str_set(&s->out, smtp_ok);

    return NGX_OK;

}

static ngx_int_t

‌ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)

{

#if (NGX_MAIL_SSL)

    ngx_mail_ssl_conf_t  *sslcf;

    if (c->ssl == NULL) {

        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

        if (sslcf->starttls) {

            /*

             * RFC3207 requires us to discard any knowledge

             * obtained from client before STARTTLS.

             */

            ngx_str_null(&s->smtp_helo);

            ngx_str_null(&s->smtp_from);

            ngx_str_null(&s->smtp_to);

            s->buffer->pos = s->buffer->start;

            s->buffer->last = s->buffer->start;

            c->read->handler = ngx_mail_starttls_handler;

            return NGX_OK;

        }

    }

#endif

    return NGX_MAIL_PARSE_INVALID_COMMAND;

}

static ngx_int_t

‌ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,

    char *err)

{

    ssize_t    n;

    n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);

    if (n == NGX_ERROR || n == 0) {

        ngx_mail_close_connection(c);

        return NGX_ERROR;

    }

    if (n > 0) {

        s->buffer->last += n;

    }

    if (n == NGX_AGAIN) {

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            ngx_mail_session_internal_server_error(s);

            return NGX_ERROR;

        }

        return NGX_AGAIN;

    }

    ngx_mail_smtp_log_rejected_command(s, c, err);

    s->buffer->pos = s->buffer->start;

    s->buffer->last = s->buffer->start;

    return NGX_OK;

}

static void

‌ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,

    char *err)

{

    u_char      ch;

    ngx_str_t   cmd;

    ngx_uint_t  i;

    if (c->log->log_level < NGX_LOG_INFO) {

        return;

    }

    cmd.len = s->buffer->last - s->buffer->start;

    cmd.data = s->buffer->start;

    for (i = 0; i < cmd.len; i++) {

        ch = cmd.data[i];

        if (ch != CR && ch != LF) {

            continue;

        }

        cmd.data[i] = '_';

    }

    cmd.len = i;

    ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);

}

src/mail/ngx_mail_smtp_module.h - nginx-1.7.9

Data types defined

ngx_mail_smtp_srv_conf_t

Macros defined

_NGX_MAIL_SMTP_MODULE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_

‌#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_mail.h>

#include <ngx_mail_smtp_module.h>

typedef struct {

    ngx_msec_t   greeting_delay;

    size_t       client_buffer_size;

    ngx_str_t    capability;

    ngx_str_t    starttls_capability;

    ngx_str_t    starttls_only_capability;

    ngx_str_t    server_name;

    ngx_str_t    greeting;

    ngx_uint_t   auth_methods;

    ngx_array_t  capabilities;

‌} ngx_mail_smtp_srv_conf_t;

void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c);

void ngx_mail_smtp_init_protocol(ngx_event_t *rev);

void ngx_mail_smtp_auth_state(ngx_event_t *rev);

ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s);

extern ngx_module_t  ngx_mail_smtp_module;

#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */

src/core/ngx_proxy_protocol.c - nginx-1.7.9

Functions defined

ngx_proxy_protocol_parse

Source code

/*

* Copyright (C) Roman Arutyunyan

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

u_char *

‌ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf, u_char *last)

{

    size_t  len;

    u_char  ch, *p, *addr;

    p = buf;

    len = last - buf;

    if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) {

        goto invalid;

    }

    p += 6;

    len -= 6;

    if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) {

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,

                       "PROXY protocol unknown protocol");

        p += 7;

        goto skip;

    }

    if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0

        || (p[3] != '4' && p[3] != '6') || p[4] != ' ')

    {

        goto invalid;

    }

    p += 5;

    addr = p;

    for ( ;; ) {

        if (p == last) {

            goto invalid;

        }

        ch = *p++;

        if (ch == ' ') {

            break;

        }

        if (ch != ':' && ch != '.'

            && (ch < 'a' || ch > 'f')

            && (ch < 'A' || ch > 'F')

            && (ch < '0' || ch > '9'))

        {

            goto invalid;

        }

    }

    len = p - addr - 1;

    c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, len);

    if (c->proxy_protocol_addr.data == NULL) {

        return NULL;

    }

    ngx_memcpy(c->proxy_protocol_addr.data, addr, len);

    c->proxy_protocol_addr.len = len;

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,

                   "PROXY protocol address: \"%V\"", &c->proxy_protocol_addr);

skip:

    for ( /* void */ ; p < last - 1; p++) {

        if (p[0] == CR && p[1] == LF) {

            return p + 2;

        }

    }

invalid:

    ngx_log_error(NGX_LOG_ERR, c->log, 0,

                  "broken header: \"%*s\"", (size_t) (last - buf), buf);

    return NULL;

}

src/core/ngx_proxy_protocol.h - nginx-1.7.9

Macros defined

NGX_PROXY_PROTOCOL_MAX_HEADER

_NGX_PROXY_PROTOCOL_H_INCLUDED_

Source code

/*

* Copyright (C) Roman Arutyunyan

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_

‌#define _NGX_PROXY_PROTOCOL_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_PROXY_PROTOCOL_MAX_HEADER  107

u_char *ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf,

    u_char *last);

#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */

src/core/ngx_regex.h - nginx-1.7.9

Data types defined

ngx_regex_compile_t

ngx_regex_elt_t

ngx_regex_t

Macros defined

NGX_REGEX_CASELESS

NGX_REGEX_NO_MATCHED

_NGX_REGEX_H_INCLUDED_

ngx_regex_exec

ngx_regex_exec_n

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_REGEX_H_INCLUDED_

‌#define _NGX_REGEX_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <pcre.h>

‌#define NGX_REGEX_NO_MATCHED  PCRE_ERROR_NOMATCH   /* -1 */

‌#define NGX_REGEX_CASELESS    PCRE_CASELESS

typedef struct {

    pcre        *code;

    pcre_extra  *extra;

‌} ngx_regex_t;

typedef struct {

    ngx_str_t     pattern;

    ngx_pool_t   *pool;

    ngx_int_t     options;

    ngx_regex_t  *regex;

    int           captures;

    int           named_captures;

    int           name_size;

    u_char       *names;

    ngx_str_t     err;

‌} ngx_regex_compile_t;

typedef struct {

    ngx_regex_t  *regex;

    u_char       *name;

‌} ngx_regex_elt_t;

void ngx_regex_init(void);

ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc);

‌#define ngx_regex_exec(re, s, captures, size)                                \

    pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \

              captures, size)

‌#define ngx_regex_exec_n      "pcre_exec()"

ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);

#endif /* _NGX_REGEX_H_INCLUDED_ */

src/core/ngx_regex.c - nginx-1.7.9

Global variables defined

ngx_pcre_pool

ngx_pcre_studies

ngx_regex_commands

ngx_regex_module

ngx_regex_module_ctx

ngx_regex_pcre_jit_post

Data types defined

ngx_regex_conf_t

Functions defined

ngx_pcre_free_studies

ngx_regex_compile

ngx_regex_create_conf

ngx_regex_exec_array

ngx_regex_free

ngx_regex_init

ngx_regex_init_conf

ngx_regex_malloc

ngx_regex_malloc_done

ngx_regex_malloc_init

ngx_regex_module_init

ngx_regex_pcre_jit

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

typedef struct {

    ngx_flag_t  pcre_jit;

‌} ngx_regex_conf_t;

static void * ngx_libc_cdecl ngx_regex_malloc(size_t size);

static void ngx_libc_cdecl ngx_regex_free(void *p);

#if (NGX_HAVE_PCRE_JIT)

static void ngx_pcre_free_studies(void *data);

#endif

static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle);

static void *ngx_regex_create_conf(ngx_cycle_t *cycle);

static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf);

static char *ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data);

‌static ngx_conf_post_t  ngx_regex_pcre_jit_post = { ngx_regex_pcre_jit };

‌static ngx_command_t  ngx_regex_commands[] = {

    { ngx_string("pcre_jit"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_flag_slot,

      0,

      offsetof(ngx_regex_conf_t, pcre_jit),

      &ngx_regex_pcre_jit_post },

      ngx_null_command

};

‌static ngx_core_module_t  ngx_regex_module_ctx = {

    ngx_string("regex"),

    ngx_regex_create_conf,

    ngx_regex_init_conf

};

‌ngx_module_t  ngx_regex_module = {

    NGX_MODULE_V1,

    &ngx_regex_module_ctx,                 /* module context */

    ngx_regex_commands,                    /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    ngx_regex_module_init,                 /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_pool_t  *ngx_pcre_pool;

‌static ngx_list_t  *ngx_pcre_studies;

void

‌ngx_regex_init(void)

{

    pcre_malloc = ngx_regex_malloc;

    pcre_free = ngx_regex_free;

}

static ngx_inline void

‌ngx_regex_malloc_init(ngx_pool_t *pool)

{

#if (NGX_THREADS)

    ngx_core_tls_t  *tls;

    if (ngx_threaded) {

        tls = ngx_thread_get_tls(ngx_core_tls_key);

        tls->pool = pool;

        return;

    }

#endif

    ngx_pcre_pool = pool;

}

static ngx_inline void

‌ngx_regex_malloc_done(void)

{

#if (NGX_THREADS)

    ngx_core_tls_t  *tls;

    if (ngx_threaded) {

        tls = ngx_thread_get_tls(ngx_core_tls_key);

        tls->pool = NULL;

        return;

    }

#endif

    ngx_pcre_pool = NULL;

}

ngx_int_t

‌ngx_regex_compile(ngx_regex_compile_t *rc)

{

    int               n, erroff;

    char             *p;

    pcre             *re;

    const char       *errstr;

    ngx_regex_elt_t  *elt;

    ngx_regex_malloc_init(rc->pool);

    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,

                      &errstr, &erroff, NULL);

    /* ensure that there is no current pool */

    ngx_regex_malloc_done();

    if (re == NULL) {

        if ((size_t) erroff == rc->pattern.len) {

           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,

                              "pcre_compile() failed: %s in \"%V\"",

                               errstr, &rc->pattern)

                      - rc->err.data;

        } else {

           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,

                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",

                               errstr, &rc->pattern, rc->pattern.data + erroff)

                      - rc->err.data;

        }

        return NGX_ERROR;

    }

    rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t));

    if (rc->regex == NULL) {

        goto nomem;

    }

    rc->regex->code = re;

    /* do not study at runtime */

    if (ngx_pcre_studies != NULL) {

        elt = ngx_list_push(ngx_pcre_studies);

        if (elt == NULL) {

            goto nomem;

        }

        elt->regex = rc->regex;

        elt->name = rc->pattern.data;

    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);

    if (n < 0) {

        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";

        goto failed;

    }

    if (rc->captures == 0) {

        return NGX_OK;

    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);

    if (n < 0) {

        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";

        goto failed;

    }

    if (rc->named_captures == 0) {

        return NGX_OK;

    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);

    if (n < 0) {

        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";

        goto failed;

    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);

    if (n < 0) {

        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";

        goto failed;

    }

    return NGX_OK;

failed:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)

                  - rc->err.data;

    return NGX_ERROR;

nomem:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,

                               "regex \"%V\" compilation failed: no memory",

                               &rc->pattern)

                  - rc->err.data;

    return NGX_ERROR;

}

ngx_int_t

‌ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)

{

    ngx_int_t         n;

    ngx_uint_t        i;

    ngx_regex_elt_t  *re;

    re = a->elts;

    for (i = 0; i < a->nelts; i++) {

        n = ngx_regex_exec(re[i].regex, s, NULL, 0);

        if (n == NGX_REGEX_NO_MATCHED) {

            continue;

        }

        if (n < 0) {

            ngx_log_error(NGX_LOG_ALERT, log, 0,

                          ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"",

                          n, s, re[i].name);

            return NGX_ERROR;

        }

        /* match */

        return NGX_OK;

    }

    return NGX_DECLINED;

}

static void * ngx_libc_cdecl

‌ngx_regex_malloc(size_t size)

{

    ngx_pool_t      *pool;

#if (NGX_THREADS)

    ngx_core_tls_t  *tls;

    if (ngx_threaded) {

        tls = ngx_thread_get_tls(ngx_core_tls_key);

        pool = tls->pool;

    } else {

        pool = ngx_pcre_pool;

    }

#else

    pool = ngx_pcre_pool;

#endif

    if (pool) {

        return ngx_palloc(pool, size);

    }

    return NULL;

}

static void ngx_libc_cdecl

‌ngx_regex_free(void *p)

{

    return;

}

#if (NGX_HAVE_PCRE_JIT)

static void

‌ngx_pcre_free_studies(void *data)

{

    ngx_list_t *studies = data;

    ngx_uint_t        i;

    ngx_list_part_t  *part;

    ngx_regex_elt_t  *elts;

    part = &studies->part;

    elts = part->elts;

    for (i = 0 ; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            elts = part->elts;

            i = 0;

        }

        if (elts[i].regex->extra != NULL) {

            pcre_free_study(elts[i].regex->extra);

        }

    }

}

#endif

static ngx_int_t

‌ngx_regex_module_init(ngx_cycle_t *cycle)

{

    int               opt;

    const char       *errstr;

    ngx_uint_t        i;

    ngx_list_part_t  *part;

    ngx_regex_elt_t  *elts;

    opt = 0;

#if (NGX_HAVE_PCRE_JIT)

    {

    ngx_regex_conf_t    *rcf;

    ngx_pool_cleanup_t  *cln;

    rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);

    if (rcf->pcre_jit) {

        opt = PCRE_STUDY_JIT_COMPILE;

        /*

         * The PCRE JIT compiler uses mmap for its executable codes, so we

         * have to explicitly call the pcre_free_study() function to free

         * this memory.

         */

        cln = ngx_pool_cleanup_add(cycle->pool, 0);

        if (cln == NULL) {

            return NGX_ERROR;

        }

        cln->handler = ngx_pcre_free_studies;

        cln->data = ngx_pcre_studies;

    }

    }

#endif

    ngx_regex_malloc_init(cycle->pool);

    part = &ngx_pcre_studies->part;

    elts = part->elts;

    for (i = 0 ; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            elts = part->elts;

            i = 0;

        }

        elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr);

        if (errstr != NULL) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "pcre_study() failed: %s in \"%s\"",

                          errstr, elts[i].name);

        }

#if (NGX_HAVE_PCRE_JIT)

        if (opt & PCRE_STUDY_JIT_COMPILE) {

            int jit, n;

            jit = 0;

            n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra,

                              PCRE_INFO_JIT, &jit);

            if (n != 0 || jit != 1) {

                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,

                              "JIT compiler does not support pattern: \"%s\"",

                              elts[i].name);

            }

        }

#endif

    }

    ngx_regex_malloc_done();

    ngx_pcre_studies = NULL;

    return NGX_OK;

}

static void *

‌ngx_regex_create_conf(ngx_cycle_t *cycle)

{

    ngx_regex_conf_t  *rcf;

    rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t));

    if (rcf == NULL) {

        return NULL;

    }

    rcf->pcre_jit = NGX_CONF_UNSET;

    ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));

    if (ngx_pcre_studies == NULL) {

        return NULL;

    }

    return rcf;

}

static char *

‌ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_regex_conf_t *rcf = conf;

    ngx_conf_init_value(rcf->pcre_jit, 0);

    return NGX_CONF_OK;

}

static char *

‌ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data)

{

    ngx_flag_t  *fp = data;

    if (*fp == 0) {

        return NGX_CONF_OK;

    }

#if (NGX_HAVE_PCRE_JIT)

    {

    int  jit, r;

    jit = 0;

    r = pcre_config(PCRE_CONFIG_JIT, &jit);

    if (r != 0 || jit != 1) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "PCRE library does not support JIT");

        *fp = 0;

    }

    }

#else

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "nginx was built without PCRE JIT support");

    *fp = 0;

#endif

    return NGX_CONF_OK;

}

src/http/ngx_http_special_response.c - nginx-1.7.9

Global variables defined

ngx_http_error_301_page

ngx_http_error_302_page

ngx_http_error_303_page

ngx_http_error_307_page

ngx_http_error_400_page

ngx_http_error_401_page

ngx_http_error_402_page

ngx_http_error_403_page

ngx_http_error_404_page

ngx_http_error_405_page

ngx_http_error_406_page

ngx_http_error_408_page

ngx_http_error_409_page

ngx_http_error_410_page

ngx_http_error_411_page

ngx_http_error_412_page

ngx_http_error_413_page

ngx_http_error_414_page

ngx_http_error_415_page

ngx_http_error_416_page

ngx_http_error_494_page

ngx_http_error_495_page

ngx_http_error_496_page

ngx_http_error_497_page

ngx_http_error_500_page

ngx_http_error_501_page

ngx_http_error_502_page

ngx_http_error_503_page

ngx_http_error_504_page

ngx_http_error_507_page

ngx_http_error_full_tail

ngx_http_error_pages

ngx_http_error_tail

ngx_http_get_name

ngx_http_msie_padding

ngx_http_msie_refresh_head

ngx_http_msie_refresh_tail

Functions defined

ngx_http_clean_header

ngx_http_filter_finalize_request

ngx_http_send_error_page

ngx_http_send_refresh

ngx_http_send_special_response

ngx_http_special_response_handler

Macros defined

NGX_HTTP_LAST_2XX

NGX_HTTP_LAST_3XX

NGX_HTTP_LAST_4XX

NGX_HTTP_LAST_5XX

NGX_HTTP_OFF_3XX

NGX_HTTP_OFF_4XX

NGX_HTTP_OFF_5XX

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <nginx.h>

static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,

    ngx_http_err_page_t *err_page);

static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);

static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);

‌static u_char ngx_http_error_full_tail[] =

"<hr><center>" NGINX_VER "</center>" CRLF

"</body>" CRLF

"</html>" CRLF

;

‌static u_char ngx_http_error_tail[] =

"<hr><center>nginx</center>" CRLF

"</body>" CRLF

"</html>" CRLF

;

‌static u_char ngx_http_msie_padding[] =

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF

;

‌static u_char ngx_http_msie_refresh_head[] =

"<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=";

‌static u_char ngx_http_msie_refresh_tail[] =

"\"></head><body></body></html>" CRLF;

‌static char ngx_http_error_301_page[] =

"<html>" CRLF

"<head><title>301 Moved Permanently</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>301 Moved Permanently</h1></center>" CRLF

;

‌static char ngx_http_error_302_page[] =

"<html>" CRLF

"<head><title>302 Found</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>302 Found</h1></center>" CRLF

;

‌static char ngx_http_error_303_page[] =

"<html>" CRLF

"<head><title>303 See Other</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>303 See Other</h1></center>" CRLF

;

‌static char ngx_http_error_307_page[] =

"<html>" CRLF

"<head><title>307 Temporary Redirect</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>307 Temporary Redirect</h1></center>" CRLF

;

‌static char ngx_http_error_400_page[] =

"<html>" CRLF

"<head><title>400 Bad Request</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>400 Bad Request</h1></center>" CRLF

;

‌static char ngx_http_error_401_page[] =

"<html>" CRLF

"<head><title>401 Authorization Required</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>401 Authorization Required</h1></center>" CRLF

;

‌static char ngx_http_error_402_page[] =

"<html>" CRLF

"<head><title>402 Payment Required</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>402 Payment Required</h1></center>" CRLF

;

‌static char ngx_http_error_403_page[] =

"<html>" CRLF

"<head><title>403 Forbidden</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>403 Forbidden</h1></center>" CRLF

;

‌static char ngx_http_error_404_page[] =

"<html>" CRLF

"<head><title>404 Not Found</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>404 Not Found</h1></center>" CRLF

;

‌static char ngx_http_error_405_page[] =

"<html>" CRLF

"<head><title>405 Not Allowed</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>405 Not Allowed</h1></center>" CRLF

;

‌static char ngx_http_error_406_page[] =

"<html>" CRLF

"<head><title>406 Not Acceptable</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>406 Not Acceptable</h1></center>" CRLF

;

‌static char ngx_http_error_408_page[] =

"<html>" CRLF

"<head><title>408 Request Time-out</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>408 Request Time-out</h1></center>" CRLF

;

‌static char ngx_http_error_409_page[] =

"<html>" CRLF

"<head><title>409 Conflict</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>409 Conflict</h1></center>" CRLF

;

‌static char ngx_http_error_410_page[] =

"<html>" CRLF

"<head><title>410 Gone</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>410 Gone</h1></center>" CRLF

;

‌static char ngx_http_error_411_page[] =

"<html>" CRLF

"<head><title>411 Length Required</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>411 Length Required</h1></center>" CRLF

;

‌static char ngx_http_error_412_page[] =

"<html>" CRLF

"<head><title>412 Precondition Failed</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>412 Precondition Failed</h1></center>" CRLF

;

‌static char ngx_http_error_413_page[] =

"<html>" CRLF

"<head><title>413 Request Entity Too Large</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>413 Request Entity Too Large</h1></center>" CRLF

;

‌static char ngx_http_error_414_page[] =

"<html>" CRLF

"<head><title>414 Request-URI Too Large</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>414 Request-URI Too Large</h1></center>" CRLF

;

‌static char ngx_http_error_415_page[] =

"<html>" CRLF

"<head><title>415 Unsupported Media Type</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>415 Unsupported Media Type</h1></center>" CRLF

;

‌static char ngx_http_error_416_page[] =

"<html>" CRLF

"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF

;

‌static char ngx_http_error_494_page[] =

"<html>" CRLF

"<head><title>400 Request Header Or Cookie Too Large</title></head>"

CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>400 Bad Request</h1></center>" CRLF

"<center>Request Header Or Cookie Too Large</center>" CRLF

;

‌static char ngx_http_error_495_page[] =

"<html>" CRLF

"<head><title>400 The SSL certificate error</title></head>"

CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>400 Bad Request</h1></center>" CRLF

"<center>The SSL certificate error</center>" CRLF

;

‌static char ngx_http_error_496_page[] =

"<html>" CRLF

"<head><title>400 No required SSL certificate was sent</title></head>"

CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>400 Bad Request</h1></center>" CRLF

"<center>No required SSL certificate was sent</center>" CRLF

;

‌static char ngx_http_error_497_page[] =

"<html>" CRLF

"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"

CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>400 Bad Request</h1></center>" CRLF

"<center>The plain HTTP request was sent to HTTPS port</center>" CRLF

;

‌static char ngx_http_error_500_page[] =

"<html>" CRLF

"<head><title>500 Internal Server Error</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>500 Internal Server Error</h1></center>" CRLF

;

‌static char ngx_http_error_501_page[] =

"<html>" CRLF

"<head><title>501 Not Implemented</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>501 Not Implemented</h1></center>" CRLF

;

‌static char ngx_http_error_502_page[] =

"<html>" CRLF

"<head><title>502 Bad Gateway</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>502 Bad Gateway</h1></center>" CRLF

;

‌static char ngx_http_error_503_page[] =

"<html>" CRLF

"<head><title>503 Service Temporarily Unavailable</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF

;

‌static char ngx_http_error_504_page[] =

"<html>" CRLF

"<head><title>504 Gateway Time-out</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>504 Gateway Time-out</h1></center>" CRLF

;

‌static char ngx_http_error_507_page[] =

"<html>" CRLF

"<head><title>507 Insufficient Storage</title></head>" CRLF

"<body bgcolor=\"white\">" CRLF

"<center><h1>507 Insufficient Storage</h1></center>" CRLF

;

‌static ngx_str_t ngx_http_error_pages[] = {

    ngx_null_string,                     /* 201, 204 */

‌#define NGX_HTTP_LAST_2XX  202

‌#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 201)

    /* ngx_null_string, */               /* 300 */

    ngx_string(ngx_http_error_301_page),

    ngx_string(ngx_http_error_302_page),

    ngx_string(ngx_http_error_303_page),

    ngx_null_string,                     /* 304 */

    ngx_null_string,                     /* 305 */

    ngx_null_string,                     /* 306 */

    ngx_string(ngx_http_error_307_page),

‌#define NGX_HTTP_LAST_3XX  308

‌#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)

    ngx_string(ngx_http_error_400_page),

    ngx_string(ngx_http_error_401_page),

    ngx_string(ngx_http_error_402_page),

    ngx_string(ngx_http_error_403_page),

    ngx_string(ngx_http_error_404_page),

    ngx_string(ngx_http_error_405_page),

    ngx_string(ngx_http_error_406_page),

    ngx_null_string,                     /* 407 */

    ngx_string(ngx_http_error_408_page),

    ngx_string(ngx_http_error_409_page),

    ngx_string(ngx_http_error_410_page),

    ngx_string(ngx_http_error_411_page),

    ngx_string(ngx_http_error_412_page),

    ngx_string(ngx_http_error_413_page),

    ngx_string(ngx_http_error_414_page),

    ngx_string(ngx_http_error_415_page),

    ngx_string(ngx_http_error_416_page),

‌#define NGX_HTTP_LAST_4XX  417

‌#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)

    ngx_string(ngx_http_error_494_page), /* 494, request header too large */

    ngx_string(ngx_http_error_495_page), /* 495, https certificate error */

    ngx_string(ngx_http_error_496_page), /* 496, https no certificate */

    ngx_string(ngx_http_error_497_page), /* 497, http to https */

    ngx_string(ngx_http_error_404_page), /* 498, canceled */

    ngx_null_string,                     /* 499, client has closed connection */

    ngx_string(ngx_http_error_500_page),

    ngx_string(ngx_http_error_501_page),

    ngx_string(ngx_http_error_502_page),

    ngx_string(ngx_http_error_503_page),

    ngx_string(ngx_http_error_504_page),

    ngx_null_string,                     /* 505 */

    ngx_null_string,                     /* 506 */

    ngx_string(ngx_http_error_507_page)

‌#define NGX_HTTP_LAST_5XX  508

};

‌static ngx_str_t  ngx_http_get_name = { 3, (u_char *) "GET " };

ngx_int_t

‌ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)

{

    ngx_uint_t                 i, err;

    ngx_http_err_page_t       *err_page;

    ngx_http_core_loc_conf_t  *clcf;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http special response: %i, \"%V?%V\"",

                   error, &r->uri, &r->args);

    r->err_status = error;

    if (r->keepalive) {

        switch (error) {

            case NGX_HTTP_BAD_REQUEST:

            case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:

            case NGX_HTTP_REQUEST_URI_TOO_LARGE:

            case NGX_HTTP_TO_HTTPS:

            case NGX_HTTPS_CERT_ERROR:

            case NGX_HTTPS_NO_CERT:

            case NGX_HTTP_INTERNAL_SERVER_ERROR:

            case NGX_HTTP_NOT_IMPLEMENTED:

                r->keepalive = 0;

        }

    }

    if (r->lingering_close) {

        switch (error) {

            case NGX_HTTP_BAD_REQUEST:

            case NGX_HTTP_TO_HTTPS:

            case NGX_HTTPS_CERT_ERROR:

            case NGX_HTTPS_NO_CERT:

                r->lingering_close = 0;

        }

    }

    r->headers_out.content_type.len = 0;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {

        if (clcf->recursive_error_pages == 0) {

            r->error_page = 1;

        }

        err_page = clcf->error_pages->elts;

        for (i = 0; i < clcf->error_pages->nelts; i++) {

            if (err_page[i].status == error) {

                return ngx_http_send_error_page(r, &err_page[i]);

            }

        }

    }

    r->expect_tested = 1;

    if (ngx_http_discard_request_body(r) != NGX_OK) {

        r->keepalive = 0;

    }

    if (clcf->msie_refresh

        && r->headers_in.msie

        && (error == NGX_HTTP_MOVED_PERMANENTLY

            || error == NGX_HTTP_MOVED_TEMPORARILY))

    {

        return ngx_http_send_refresh(r);

    }

    if (error == NGX_HTTP_CREATED) {

        /* 201 */

        err = 0;

    } else if (error == NGX_HTTP_NO_CONTENT) {

        /* 204 */

        err = 0;

    } else if (error >= NGX_HTTP_MOVED_PERMANENTLY

               && error < NGX_HTTP_LAST_3XX)

    {

        /* 3XX */

        err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;

    } else if (error >= NGX_HTTP_BAD_REQUEST

               && error < NGX_HTTP_LAST_4XX)

    {

        /* 4XX */

        err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;

    } else if (error >= NGX_HTTP_NGINX_CODES

               && error < NGX_HTTP_LAST_5XX)

    {

        /* 49X, 5XX */

        err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;

        switch (error) {

            case NGX_HTTP_TO_HTTPS:

            case NGX_HTTPS_CERT_ERROR:

            case NGX_HTTPS_NO_CERT:

            case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:

                r->err_status = NGX_HTTP_BAD_REQUEST;

                break;

        }

    } else {

        /* unknown code, zero body */

        err = 0;

    }

    return ngx_http_send_special_response(r, clcf, err);

}

ngx_int_t

‌ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,

    ngx_int_t error)

{

    void       *ctx;

    ngx_int_t   rc;

    ngx_http_clean_header(r);

    ctx = NULL;

    if (m) {

        ctx = r->ctx[m->ctx_index];

    }

    /* clear the modules contexts */

    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);

    if (m) {

        r->ctx[m->ctx_index] = ctx;

    }

    r->filter_finalize = 1;

    rc = ngx_http_special_response_handler(r, error);

    /* NGX_ERROR resets any pending data */

    switch (rc) {

    case NGX_OK:

    case NGX_DONE:

        return NGX_ERROR;

    default:

        return rc;

    }

}

void

‌ngx_http_clean_header(ngx_http_request_t *r)

{

    ngx_memzero(&r->headers_out.status,

                sizeof(ngx_http_headers_out_t)

                    - offsetof(ngx_http_headers_out_t, status));

    r->headers_out.headers.part.nelts = 0;

    r->headers_out.headers.part.next = NULL;

    r->headers_out.headers.last = &r->headers_out.headers.part;

    r->headers_out.content_length_n = -1;

    r->headers_out.last_modified_time = -1;

}

static ngx_int_t

‌ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)

{

    ngx_int_t                  overwrite;

    ngx_str_t                  uri, args;

    ngx_table_elt_t           *location;

    ngx_http_core_loc_conf_t  *clcf;

    overwrite = err_page->overwrite;

    if (overwrite && overwrite != NGX_HTTP_OK) {

        r->expect_tested = 1;

    }

    if (overwrite >= 0) {

        r->err_status = overwrite;

    }

    if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {

        return NGX_ERROR;

    }

    if (uri.data[0] == '/') {

        if (err_page->value.lengths) {

            ngx_http_split_args(r, &uri, &args);

        } else {

            args = err_page->args;

        }

        if (r->method != NGX_HTTP_HEAD) {

            r->method = NGX_HTTP_GET;

            r->method_name = ngx_http_get_name;

        }

        return ngx_http_internal_redirect(r, &uri, &args);

    }

    if (uri.data[0] == '@') {

        return ngx_http_named_location(r, &uri);

    }

    location = ngx_list_push(&r->headers_out.headers);

    if (location == NULL) {

        return NGX_ERROR;

    }

    if (overwrite != NGX_HTTP_MOVED_PERMANENTLY

        && overwrite != NGX_HTTP_MOVED_TEMPORARILY

        && overwrite != NGX_HTTP_SEE_OTHER

        && overwrite != NGX_HTTP_TEMPORARY_REDIRECT)

    {

        r->err_status = NGX_HTTP_MOVED_TEMPORARILY;

    }

    location->hash = 1;

    ngx_str_set(&location->key, "Location");

    location->value = uri;

    ngx_http_clear_location(r);

    r->headers_out.location = location;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->msie_refresh && r->headers_in.msie) {

        return ngx_http_send_refresh(r);

    }

    return ngx_http_send_special_response(r, clcf, r->err_status

                                                   - NGX_HTTP_MOVED_PERMANENTLY

                                                   + NGX_HTTP_OFF_3XX);

}

static ngx_int_t

‌ngx_http_send_special_response(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)

{

    u_char       *tail;

    size_t        len;

    ngx_int_t     rc;

    ngx_buf_t    *b;

    ngx_uint_t    msie_padding;

    ngx_chain_t   out[3];

    if (clcf->server_tokens) {

        len = sizeof(ngx_http_error_full_tail) - 1;

        tail = ngx_http_error_full_tail;

    } else {

        len = sizeof(ngx_http_error_tail) - 1;

        tail = ngx_http_error_tail;

    }

    msie_padding = 0;

    if (ngx_http_error_pages[err].len) {

        r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;

        if (clcf->msie_padding

            && (r->headers_in.msie || r->headers_in.chrome)

            && r->http_version >= NGX_HTTP_VERSION_10

            && err >= NGX_HTTP_OFF_4XX)

        {

            r->headers_out.content_length_n +=

                                         sizeof(ngx_http_msie_padding) - 1;

            msie_padding = 1;

        }

        r->headers_out.content_type_len = sizeof("text/html") - 1;

        ngx_str_set(&r->headers_out.content_type, "text/html");

        r->headers_out.content_type_lowcase = NULL;

    } else {

        r->headers_out.content_length_n = 0;

    }

    if (r->headers_out.content_length) {

        r->headers_out.content_length->hash = 0;

        r->headers_out.content_length = NULL;

    }

    ngx_http_clear_accept_ranges(r);

    ngx_http_clear_last_modified(r);

    ngx_http_clear_etag(r);

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || r->header_only) {

        return rc;

    }

    if (ngx_http_error_pages[err].len == 0) {

        return ngx_http_send_special(r, NGX_HTTP_LAST);

    }

    b = ngx_calloc_buf(r->pool);

    if (b == NULL) {

        return NGX_ERROR;

    }

    b->memory = 1;

    b->pos = ngx_http_error_pages[err].data;

    b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;

    out[0].buf = b;

    out[0].next = &out[1];

    b = ngx_calloc_buf(r->pool);

    if (b == NULL) {

        return NGX_ERROR;

    }

    b->memory = 1;

    b->pos = tail;

    b->last = tail + len;

    out[1].buf = b;

    out[1].next = NULL;

    if (msie_padding) {

        b = ngx_calloc_buf(r->pool);

        if (b == NULL) {

            return NGX_ERROR;

        }

        b->memory = 1;

        b->pos = ngx_http_msie_padding;

        b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;

        out[1].next = &out[2];

        out[2].buf = b;

        out[2].next = NULL;

    }

    if (r == r->main) {

        b->last_buf = 1;

    }

    b->last_in_chain = 1;

    return ngx_http_output_filter(r, &out[0]);

}

static ngx_int_t

‌ngx_http_send_refresh(ngx_http_request_t *r)

{

    u_char       *p, *location;

    size_t        len, size;

    uintptr_t     escape;

    ngx_int_t     rc;

    ngx_buf_t    *b;

    ngx_chain_t   out;

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

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

    escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);

    size = sizeof(ngx_http_msie_refresh_head) - 1

           + escape + len

           + sizeof(ngx_http_msie_refresh_tail) - 1;

    r->err_status = NGX_HTTP_OK;

    r->headers_out.content_type_len = sizeof("text/html") - 1;

    ngx_str_set(&r->headers_out.content_type, "text/html");

    r->headers_out.content_type_lowcase = NULL;

    r->headers_out.location->hash = 0;

    r->headers_out.location = NULL;

    r->headers_out.content_length_n = size;

    if (r->headers_out.content_length) {

        r->headers_out.content_length->hash = 0;

        r->headers_out.content_length = NULL;

    }

    ngx_http_clear_accept_ranges(r);

    ngx_http_clear_last_modified(r);

    ngx_http_clear_etag(r);

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || r->header_only) {

        return rc;

    }

    b = ngx_create_temp_buf(r->pool, size);

    if (b == NULL) {

        return NGX_ERROR;

    }

    p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,

                   sizeof(ngx_http_msie_refresh_head) - 1);

    if (escape == 0) {

        p = ngx_cpymem(p, location, len);

    } else {

        p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);

    }

    b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,

                         sizeof(ngx_http_msie_refresh_tail) - 1);

    b->last_buf = 1;

    b->last_in_chain = 1;

    out.buf = b;

    out.next = NULL;

    return ngx_http_output_filter(r, &out);

}

src/http/ngx_http_header_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_header_filter_module

ngx_http_header_filter_module_ctx

ngx_http_headers_out

ngx_http_server_full_string

ngx_http_server_string

ngx_http_status_lines

Functions defined

ngx_http_header_filter

ngx_http_header_filter_init

Macros defined

NGX_HTTP_LAST_2XX

NGX_HTTP_LAST_3XX

NGX_HTTP_LAST_4XX

NGX_HTTP_LAST_5XX

NGX_HTTP_OFF_3XX

NGX_HTTP_OFF_4XX

NGX_HTTP_OFF_5XX

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <nginx.h>

static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);

static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);

‌static ngx_http_module_t  ngx_http_header_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_header_filter_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 */

};

‌ngx_module_t  ngx_http_header_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_header_filter_module_ctx,    /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static char ngx_http_server_string[] = "Server: nginx" CRLF;

‌static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;

‌static ngx_str_t ngx_http_status_lines[] = {

    ngx_string("200 OK"),

    ngx_string("201 Created"),

    ngx_string("202 Accepted"),

    ngx_null_string,  /* "203 Non-Authoritative Information" */

    ngx_string("204 No Content"),

    ngx_null_string,  /* "205 Reset Content" */

    ngx_string("206 Partial Content"),

    /* ngx_null_string, */  /* "207 Multi-Status" */

‌#define NGX_HTTP_LAST_2XX  207

‌#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 200)

    /* ngx_null_string, */  /* "300 Multiple Choices" */

    ngx_string("301 Moved Permanently"),

    ngx_string("302 Moved Temporarily"),

    ngx_string("303 See Other"),

    ngx_string("304 Not Modified"),

    ngx_null_string,  /* "305 Use Proxy" */

    ngx_null_string,  /* "306 unused" */

    ngx_string("307 Temporary Redirect"),

‌#define NGX_HTTP_LAST_3XX  308

‌#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)

    ngx_string("400 Bad Request"),

    ngx_string("401 Unauthorized"),

    ngx_string("402 Payment Required"),

    ngx_string("403 Forbidden"),

    ngx_string("404 Not Found"),

    ngx_string("405 Not Allowed"),

    ngx_string("406 Not Acceptable"),

    ngx_null_string,  /* "407 Proxy Authentication Required" */

    ngx_string("408 Request Time-out"),

    ngx_string("409 Conflict"),

    ngx_string("410 Gone"),

    ngx_string("411 Length Required"),

    ngx_string("412 Precondition Failed"),

    ngx_string("413 Request Entity Too Large"),

    ngx_string("414 Request-URI Too Large"),

    ngx_string("415 Unsupported Media Type"),

    ngx_string("416 Requested Range Not Satisfiable"),

    /* ngx_null_string, */  /* "417 Expectation Failed" */

    /* ngx_null_string, */  /* "418 unused" */

    /* ngx_null_string, */  /* "419 unused" */

    /* ngx_null_string, */  /* "420 unused" */

    /* ngx_null_string, */  /* "421 unused" */

    /* ngx_null_string, */  /* "422 Unprocessable Entity" */

    /* ngx_null_string, */  /* "423 Locked" */

    /* ngx_null_string, */  /* "424 Failed Dependency" */

‌#define NGX_HTTP_LAST_4XX  417

‌#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)

    ngx_string("500 Internal Server Error"),

    ngx_string("501 Not Implemented"),

    ngx_string("502 Bad Gateway"),

    ngx_string("503 Service Temporarily Unavailable"),

    ngx_string("504 Gateway Time-out"),

    ngx_null_string,        /* "505 HTTP Version Not Supported" */

    ngx_null_string,        /* "506 Variant Also Negotiates" */

    ngx_string("507 Insufficient Storage"),

    /* ngx_null_string, */  /* "508 unused" */

    /* ngx_null_string, */  /* "509 unused" */

    /* ngx_null_string, */  /* "510 Not Extended" */

‌#define NGX_HTTP_LAST_5XX  508

};

‌ngx_http_header_out_t  ngx_http_headers_out[] = {

    { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) },

    { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) },

    { ngx_string("Content-Length"),

                 offsetof(ngx_http_headers_out_t, content_length) },

    { ngx_string("Content-Encoding"),

                 offsetof(ngx_http_headers_out_t, content_encoding) },

    { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) },

    { ngx_string("Last-Modified"),

                 offsetof(ngx_http_headers_out_t, last_modified) },

    { ngx_string("Accept-Ranges"),

                 offsetof(ngx_http_headers_out_t, accept_ranges) },

    { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) },

    { ngx_string("Cache-Control"),

                 offsetof(ngx_http_headers_out_t, cache_control) },

    { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) },

    { ngx_null_string, 0 }

};

static ngx_int_t

‌ngx_http_header_filter(ngx_http_request_t *r)

{

    u_char                    *p;

    size_t                     len;

    ngx_str_t                  host, *status_line;

    ngx_buf_t                 *b;

    ngx_uint_t                 status, i, port;

    ngx_chain_t                out;

    ngx_list_part_t           *part;

    ngx_table_elt_t           *header;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    ngx_http_core_srv_conf_t  *cscf;

    struct sockaddr_in        *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6       *sin6;

#endif

    u_char                     addr[NGX_SOCKADDR_STRLEN];

    if (r->header_sent) {

        return NGX_OK;

    }

    r->header_sent = 1;

    if (r != r->main) {

        return NGX_OK;

    }

    if (r->http_version < NGX_HTTP_VERSION_10) {

        return NGX_OK;

    }

    if (r->method == NGX_HTTP_HEAD) {

        r->header_only = 1;

    }

    if (r->headers_out.last_modified_time != -1) {

        if (r->headers_out.status != NGX_HTTP_OK

            && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT

            && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)

        {

            r->headers_out.last_modified_time = -1;

            r->headers_out.last_modified = NULL;

        }

    }

    len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1

          /* the end of the header */

          + sizeof(CRLF) - 1;

    /* status line */

    if (r->headers_out.status_line.len) {

        len += r->headers_out.status_line.len;

        status_line = &r->headers_out.status_line;

#if (NGX_SUPPRESS_WARN)

        status = 0;

#endif

    } else {

        status = r->headers_out.status;

        if (status >= NGX_HTTP_OK

            && status < NGX_HTTP_LAST_2XX)

        {

            /* 2XX */

            if (status == NGX_HTTP_NO_CONTENT) {

                r->header_only = 1;

                ngx_str_null(&r->headers_out.content_type);

                r->headers_out.last_modified_time = -1;

                r->headers_out.last_modified = NULL;

                r->headers_out.content_length = NULL;

                r->headers_out.content_length_n = -1;

            }

            status -= NGX_HTTP_OK;

            status_line = &ngx_http_status_lines[status];

            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_MOVED_PERMANENTLY

                   && status < NGX_HTTP_LAST_3XX)

        {

            /* 3XX */

            if (status == NGX_HTTP_NOT_MODIFIED) {

                r->header_only = 1;

            }

            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;

            status_line = &ngx_http_status_lines[status];

            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_BAD_REQUEST

                   && status < NGX_HTTP_LAST_4XX)

        {

            /* 4XX */

            status = status - NGX_HTTP_BAD_REQUEST

                            + NGX_HTTP_OFF_4XX;

            status_line = &ngx_http_status_lines[status];

            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR

                   && status < NGX_HTTP_LAST_5XX)

        {

            /* 5XX */

            status = status - NGX_HTTP_INTERNAL_SERVER_ERROR

                            + NGX_HTTP_OFF_5XX;

            status_line = &ngx_http_status_lines[status];

            len += ngx_http_status_lines[status].len;

        } else {

            len += NGX_INT_T_LEN + 1 /* SP */;

            status_line = NULL;

        }

        if (status_line && status_line->len == 0) {

            status = r->headers_out.status;

            len += NGX_INT_T_LEN + 1 /* SP */;

            status_line = NULL;

        }

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->headers_out.server == NULL) {

        len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:

                                     sizeof(ngx_http_server_string) - 1;

    }

    if (r->headers_out.date == NULL) {

        len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;

    }

    if (r->headers_out.content_type.len) {

        len += sizeof("Content-Type: ") - 1

               + r->headers_out.content_type.len + 2;

        if (r->headers_out.content_type_len == r->headers_out.content_type.len

            && r->headers_out.charset.len)

        {

            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;

        }

    }

    if (r->headers_out.content_length == NULL

        && r->headers_out.content_length_n >= 0)

    {

        len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2;

    }

    if (r->headers_out.last_modified == NULL

        && r->headers_out.last_modified_time != -1)

    {

        len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;

    }

    c = r->connection;

    if (r->headers_out.location

        && r->headers_out.location->value.len

        && r->headers_out.location->value.data[0] == '/')

    {

        r->headers_out.location->hash = 0;

        if (clcf->server_name_in_redirect) {

            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

            host = cscf->server_name;

        } else if (r->headers_in.server.len) {

            host = r->headers_in.server;

        } else {

            host.len = NGX_SOCKADDR_STRLEN;

            host.data = addr;

            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {

                return NGX_ERROR;

            }

        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            port = ntohs(sin6->sin6_port);

            break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        case AF_UNIX:

            port = 0;

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) c->local_sockaddr;

            port = ntohs(sin->sin_port);

            break;

        }

        len += sizeof("Location: https://") - 1

               + host.len

               + r->headers_out.location->value.len + 2;

        if (clcf->port_in_redirect) {

#if (NGX_HTTP_SSL)

            if (c->ssl)

                port = (port == 443) ? 0 : port;

            else

#endif

                port = (port == 80) ? 0 : port;

        } else {

            port = 0;

        }

        if (port) {

            len += sizeof(":65535") - 1;

        }

    } else {

        ngx_str_null(&host);

        port = 0;

    }

    if (r->chunked) {

        len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;

    }

    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {

        len += sizeof("Connection: upgrade" CRLF) - 1;

    } else if (r->keepalive) {

        len += sizeof("Connection: keep-alive" CRLF) - 1;

        /*

         * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header.

         * MSIE keeps the connection alive for about 60-65 seconds.

         * Opera keeps the connection alive very long.

         * Mozilla keeps the connection alive for N plus about 1-10 seconds.

         * Konqueror keeps the connection alive for about N seconds.

         */

        if (clcf->keepalive_header) {

            len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2;

        }

    } else {

        len += sizeof("Connection: close" CRLF) - 1;

    }

#if (NGX_HTTP_GZIP)

    if (r->gzip_vary) {

        if (clcf->gzip_vary) {

            len += sizeof("Vary: Accept-Encoding" CRLF) - 1;

        } else {

            r->gzip_vary = 0;

        }

    }

#endif

    part = &r->headers_out.headers.part;

    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0) {

            continue;

        }

        len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len

               + sizeof(CRLF) - 1;

    }

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NGX_ERROR;

    }

    /* "HTTP/1.x " */

    b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);

    /* status line */

    if (status_line) {

        b->last = ngx_copy(b->last, status_line->data, status_line->len);

    } else {

        b->last = ngx_sprintf(b->last, "%03ui ", status);

    }

    *b->last++ = CR; *b->last++ = LF;

    if (r->headers_out.server == NULL) {

        if (clcf->server_tokens) {

            p = (u_char *) ngx_http_server_full_string;

            len = sizeof(ngx_http_server_full_string) - 1;

        } else {

            p = (u_char *) ngx_http_server_string;

            len = sizeof(ngx_http_server_string) - 1;

        }

        b->last = ngx_cpymem(b->last, p, len);

    }

    if (r->headers_out.date == NULL) {

        b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1);

        b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,

                             ngx_cached_http_time.len);

        *b->last++ = CR; *b->last++ = LF;

    }

    if (r->headers_out.content_type.len) {

        b->last = ngx_cpymem(b->last, "Content-Type: ",

                             sizeof("Content-Type: ") - 1);

        p = b->last;

        b->last = ngx_copy(b->last, r->headers_out.content_type.data,

                           r->headers_out.content_type.len);

        if (r->headers_out.content_type_len == r->headers_out.content_type.len

            && r->headers_out.charset.len)

        {

            b->last = ngx_cpymem(b->last, "; charset=",

                                 sizeof("; charset=") - 1);

            b->last = ngx_copy(b->last, r->headers_out.charset.data,

                               r->headers_out.charset.len);

            /* update r->headers_out.content_type for possible logging */

            r->headers_out.content_type.len = b->last - p;

            r->headers_out.content_type.data = p;

        }

        *b->last++ = CR; *b->last++ = LF;

    }

    if (r->headers_out.content_length == NULL

        && r->headers_out.content_length_n >= 0)

    {

        b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,

                              r->headers_out.content_length_n);

    }

    if (r->headers_out.last_modified == NULL

        && r->headers_out.last_modified_time != -1)

    {

        b->last = ngx_cpymem(b->last, "Last-Modified: ",

                             sizeof("Last-Modified: ") - 1);

        b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);

        *b->last++ = CR; *b->last++ = LF;

    }

    if (host.data) {

        p = b->last + sizeof("Location: ") - 1;

        b->last = ngx_cpymem(b->last, "Location: http",

                             sizeof("Location: http") - 1);

#if (NGX_HTTP_SSL)

        if (c->ssl) {

            *b->last++ ='s';

        }

#endif

        *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';

        b->last = ngx_copy(b->last, host.data, host.len);

        if (port) {

            b->last = ngx_sprintf(b->last, ":%ui", port);

        }

        b->last = ngx_copy(b->last, r->headers_out.location->value.data,

                           r->headers_out.location->value.len);

        /* update r->headers_out.location->value for possible logging */

        r->headers_out.location->value.len = b->last - p;

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

        ngx_str_set(&r->headers_out.location->key, "Location");

        *b->last++ = CR; *b->last++ = LF;

    }

    if (r->chunked) {

        b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF,

                             sizeof("Transfer-Encoding: chunked" CRLF) - 1);

    }

    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {

        b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,

                             sizeof("Connection: upgrade" CRLF) - 1);

    } else if (r->keepalive) {

        b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,

                             sizeof("Connection: keep-alive" CRLF) - 1);

        if (clcf->keepalive_header) {

            b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,

                                  clcf->keepalive_header);

        }

    } else {

        b->last = ngx_cpymem(b->last, "Connection: close" CRLF,

                             sizeof("Connection: close" CRLF) - 1);

    }

#if (NGX_HTTP_GZIP)

    if (r->gzip_vary) {

        b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,

                             sizeof("Vary: Accept-Encoding" CRLF) - 1);

    }

#endif

    part = &r->headers_out.headers.part;

    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0) {

            continue;

        }

        b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);

        *b->last++ = ':'; *b->last++ = ' ';

        b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);

        *b->last++ = CR; *b->last++ = LF;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "%*s", (size_t) (b->last - b->pos), b->pos);

    /* the end of HTTP header */

    *b->last++ = CR; *b->last++ = LF;

    r->header_size = b->last - b->pos;

    if (r->header_only) {

        b->last_buf = 1;

    }

    out.buf = b;

    out.next = NULL;

    return ngx_http_write_filter(r, &out);

}

static ngx_int_t

‌ngx_http_header_filter_init(ngx_conf_t *cf)

{

    ngx_http_top_header_filter = ngx_http_header_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_userid_filter_module.c - nginx-1.7.9

Global variables defined

expires

ngx_http_next_header_filter

ngx_http_userid_commands

ngx_http_userid_domain_p

ngx_http_userid_filter_module

ngx_http_userid_filter_module_ctx

ngx_http_userid_got

ngx_http_userid_p3p_p

ngx_http_userid_path_p

ngx_http_userid_reset

ngx_http_userid_reset_index

ngx_http_userid_set

ngx_http_userid_state

sequencer_v1

sequencer_v2

start_value

Data types defined

ngx_http_userid_conf_t

ngx_http_userid_ctx_t

Functions defined

ngx_http_userid_add_variables

ngx_http_userid_create_conf

ngx_http_userid_create_uid

ngx_http_userid_domain

ngx_http_userid_expires

ngx_http_userid_filter

ngx_http_userid_get_uid

ngx_http_userid_got_variable

ngx_http_userid_init

ngx_http_userid_init_worker

ngx_http_userid_mark

ngx_http_userid_merge_conf

ngx_http_userid_p3p

ngx_http_userid_path

ngx_http_userid_reset_variable

ngx_http_userid_set_uid

ngx_http_userid_set_variable

ngx_http_userid_variable

Macros defined

NGX_HTTP_USERID_LOG

NGX_HTTP_USERID_MAX_EXPIRES

NGX_HTTP_USERID_OFF

NGX_HTTP_USERID_ON

NGX_HTTP_USERID_V1

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_USERID_OFF   0

‌#define NGX_HTTP_USERID_LOG   1

‌#define NGX_HTTP_USERID_V1    2

‌#define NGX_HTTP_USERID_ON    3

/* 31 Dec 2037 23:55:55 GMT */

‌#define NGX_HTTP_USERID_MAX_EXPIRES  2145916555

typedef struct {

    ngx_uint_t  enable;

    ngx_int_t   service;

    ngx_str_t   name;

    ngx_str_t   domain;

    ngx_str_t   path;

    ngx_str_t   p3p;

    time_t      expires;

    u_char      mark;

‌} ngx_http_userid_conf_t;

typedef struct {

    uint32_t    uid_got[4];

    uint32_t    uid_set[4];

    ngx_str_t   cookie;

    ngx_uint_t  reset;

‌} ngx_http_userid_ctx_t;

static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,

    ngx_http_userid_conf_t *conf);

static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);

static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,

    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);

static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,

    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);

static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);

static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);

static void *ngx_http_userid_create_conf(ngx_conf_t *cf);

static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);

‌static uint32_t  start_value;

‌static uint32_t  sequencer_v1 = 1;

‌static uint32_t  sequencer_v2 = 0x03030302;

‌static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT";

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_conf_enum_t  ngx_http_userid_state[] = {

    { ngx_string("off"), NGX_HTTP_USERID_OFF },

    { ngx_string("log"), NGX_HTTP_USERID_LOG },

    { ngx_string("v1"), NGX_HTTP_USERID_V1 },

    { ngx_string("on"), NGX_HTTP_USERID_ON },

    { ngx_null_string, 0 }

};

‌static ngx_conf_post_handler_pt  ngx_http_userid_domain_p =

    ngx_http_userid_domain;

‌static ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;

‌static ngx_conf_post_handler_pt  ngx_http_userid_p3p_p = ngx_http_userid_p3p;

‌static ngx_command_t  ngx_http_userid_commands[] = {

    { ngx_string("userid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, enable),

      ngx_http_userid_state },

    { ngx_string("userid_service"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, service),

      NULL },

    { ngx_string("userid_name"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, name),

      NULL },

    { ngx_string("userid_domain"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, domain),

      &ngx_http_userid_domain_p },

    { ngx_string("userid_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, path),

      &ngx_http_userid_path_p },

    { ngx_string("userid_expires"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_userid_expires,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("userid_p3p"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_userid_conf_t, p3p),

      &ngx_http_userid_p3p_p },

    { ngx_string("userid_mark"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_userid_mark,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_userid_filter_module_ctx = {

    ngx_http_userid_add_variables,         /* preconfiguration */

    ngx_http_userid_init,                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_userid_create_conf,           /* create location configuration */

    ngx_http_userid_merge_conf             /* merge location configuration */

};

‌ngx_module_t  ngx_http_userid_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_userid_filter_module_ctx,    /* module context */

    ngx_http_userid_commands,              /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    ngx_http_userid_init_worker,           /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t   ngx_http_userid_got = ngx_string("uid_got");

‌static ngx_str_t   ngx_http_userid_set = ngx_string("uid_set");

‌static ngx_str_t   ngx_http_userid_reset = ngx_string("uid_reset");

‌static ngx_uint_t  ngx_http_userid_reset_index;

static ngx_int_t

‌ngx_http_userid_filter(ngx_http_request_t *r)

{

    ngx_http_userid_ctx_t   *ctx;

    ngx_http_userid_conf_t  *conf;

    if (r != r->main) {

        return ngx_http_next_header_filter(r);

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);

    if (conf->enable < NGX_HTTP_USERID_V1) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_http_userid_get_uid(r, conf);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {

        return ngx_http_next_header_filter(r);

    }

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_userid_got_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_userid_ctx_t   *ctx;

    ngx_http_userid_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);

    if (conf->enable == NGX_HTTP_USERID_OFF) {

        v->not_found = 1;

        return NGX_OK;

    }

    ctx = ngx_http_userid_get_uid(r->main, conf);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    if (ctx->uid_got[3] != 0) {

        return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);

    }

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_userid_set_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_userid_ctx_t   *ctx;

    ngx_http_userid_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);

    if (conf->enable < NGX_HTTP_USERID_V1) {

        v->not_found = 1;

        return NGX_OK;

    }

    ctx = ngx_http_userid_get_uid(r->main, conf);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {

        return NGX_ERROR;

    }

    if (ctx->uid_set[3] == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);

}

static ngx_http_userid_ctx_t *

‌ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)

{

    ngx_int_t                n;

    ngx_str_t                src, dst;

    ngx_table_elt_t        **cookies;

    ngx_http_userid_ctx_t   *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);

    if (ctx) {

        return ctx;

    }

    if (ctx == NULL) {

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));

        if (ctx == NULL) {

            return NULL;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);

    }

    n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,

                                          &ctx->cookie);

    if (n == NGX_DECLINED) {

        return ctx;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "uid cookie: \"%V\"", &ctx->cookie);

    if (ctx->cookie.len < 22) {

        cookies = r->headers_in.cookies.elts;

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client sent too short userid cookie \"%V\"",

                      &cookies[n]->value);

        return ctx;

    }

    src = ctx->cookie;

    /*

     * we have to limit the encoded string to 22 characters because

     *  1) cookie may be marked by "userid_mark",

     *  2) and there are already the millions cookies with a garbage

     *     instead of the correct base64 trail "=="

     */

    src.len = 22;

    dst.data = (u_char *) ctx->uid_got;

    if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {

        cookies = r->headers_in.cookies.elts;

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client sent invalid userid cookie \"%V\"",

                      &cookies[n]->value);

        return ctx;

    }

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "uid: %08XD%08XD%08XD%08XD",

                   ctx->uid_got[0], ctx->uid_got[1],

                   ctx->uid_got[2], ctx->uid_got[3]);

    return ctx;

}

static ngx_int_t

‌ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,

    ngx_http_userid_conf_t *conf)

{

    u_char           *cookie, *p;

    size_t            len;

    ngx_str_t         src, dst;

    ngx_table_elt_t  *set_cookie, *p3p;

    if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {

        return NGX_ERROR;

    }

    if (ctx->uid_set[3] == 0) {

        return NGX_OK;

    }

    len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;

    if (conf->expires) {

        len += sizeof(expires) - 1 + 2;

    }

    if (conf->domain.len) {

        len += conf->domain.len;

    }

    cookie = ngx_pnalloc(r->pool, len);

    if (cookie == NULL) {

        return NGX_ERROR;

    }

    p = ngx_copy(cookie, conf->name.data, conf->name.len);

    *p++ = '=';

    if (ctx->uid_got[3] == 0 || ctx->reset) {

        src.len = 16;

        src.data = (u_char *) ctx->uid_set;

        dst.data = p;

        ngx_encode_base64(&dst, &src);

        p += dst.len;

        if (conf->mark) {

            *(p - 2) = conf->mark;

        }

    } else {

        p = ngx_cpymem(p, ctx->cookie.data, 22);

        *p++ = conf->mark;

        *p++ = '=';

    }

    if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {

        p = ngx_cpymem(p, expires, sizeof(expires) - 1);

    } else if (conf->expires) {

        p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);

        p = ngx_http_cookie_time(p, ngx_time() + conf->expires);

    }

    p = ngx_copy(p, conf->domain.data, conf->domain.len);

    p = ngx_copy(p, conf->path.data, conf->path.len);

    set_cookie = ngx_list_push(&r->headers_out.headers);

    if (set_cookie == NULL) {

        return NGX_ERROR;

    }

    set_cookie->hash = 1;

    ngx_str_set(&set_cookie->key, "Set-Cookie");

    set_cookie->value.len = p - cookie;

    set_cookie->value.data = cookie;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "uid cookie: \"%V\"", &set_cookie->value);

    if (conf->p3p.len == 0) {

        return NGX_OK;

    }

    p3p = ngx_list_push(&r->headers_out.headers);

    if (p3p == NULL) {

        return NGX_ERROR;

    }

    p3p->hash = 1;

    ngx_str_set(&p3p->key, "P3P");

    p3p->value = conf->p3p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,

    ngx_http_userid_conf_t *conf)

{

    ngx_connection_t           *c;

    struct sockaddr_in         *sin;

    ngx_http_variable_value_t  *vv;

#if (NGX_HAVE_INET6)

    u_char                     *p;

    struct sockaddr_in6        *sin6;

#endif

    if (ctx->uid_set[3] != 0) {

        return NGX_OK;

    }

    if (ctx->uid_got[3] != 0) {

        vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);

        if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {

            if (conf->mark == '\0'

                || (ctx->cookie.len > 23

                    && ctx->cookie.data[22] == conf->mark

                    && ctx->cookie.data[23] == '='))

            {

                return NGX_OK;

            }

            ctx->uid_set[0] = ctx->uid_got[0];

            ctx->uid_set[1] = ctx->uid_got[1];

            ctx->uid_set[2] = ctx->uid_got[2];

            ctx->uid_set[3] = ctx->uid_got[3];

            return NGX_OK;

        } else {

            ctx->reset = 1;

            if (vv->len == 3 && ngx_strncmp(vv->data, "log", 3) == 0) {

                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                        "userid cookie \"%V=%08XD%08XD%08XD%08XD\" was reset",

                        &conf->name, ctx->uid_got[0], ctx->uid_got[1],

                        ctx->uid_got[2], ctx->uid_got[3]);

            }

        }

    }

    /*

     * TODO: in the threaded mode the sequencers should be in TLS and their

     * ranges should be divided between threads

     */

    if (conf->enable == NGX_HTTP_USERID_V1) {

        if (conf->service == NGX_CONF_UNSET) {

            ctx->uid_set[0] = 0;

        } else {

            ctx->uid_set[0] = conf->service;

        }

        ctx->uid_set[1] = (uint32_t) ngx_time();

        ctx->uid_set[2] = start_value;

        ctx->uid_set[3] = sequencer_v1;

        sequencer_v1 += 0x100;

    } else {

        if (conf->service == NGX_CONF_UNSET) {

            c = r->connection;

            if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {

                return NGX_ERROR;

            }

            switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

            case AF_INET6:

                sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

                p = (u_char *) &ctx->uid_set[0];

                *p++ = sin6->sin6_addr.s6_addr[12];

                *p++ = sin6->sin6_addr.s6_addr[13];

                *p++ = sin6->sin6_addr.s6_addr[14];

                *p = sin6->sin6_addr.s6_addr[15];

                break;

#endif

            default: /* AF_INET */

                sin = (struct sockaddr_in *) c->local_sockaddr;

                ctx->uid_set[0] = sin->sin_addr.s_addr;

                break;

            }

        } else {

            ctx->uid_set[0] = htonl(conf->service);

        }

        ctx->uid_set[1] = htonl((uint32_t) ngx_time());

        ctx->uid_set[2] = htonl(start_value);

        ctx->uid_set[3] = htonl(sequencer_v2);

        sequencer_v2 += 0x100;

        if (sequencer_v2 < 0x03030302) {

            sequencer_v2 = 0x03030302;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    ngx_str_t *name, uint32_t *uid)

{

    v->len = name->len + sizeof("=00001111222233334444555566667777") - 1;

    v->data = ngx_pnalloc(r->pool, v->len);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",

                name, uid[0], uid[1], uid[2], uid[3]);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_userid_reset_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_userid_add_variables(ngx_conf_t *cf)

{

    ngx_int_t             n;

    ngx_http_variable_t  *var;

    var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_userid_got_variable;

    var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_userid_set_variable;

    var = ngx_http_add_variable(cf, &ngx_http_userid_reset,

                                NGX_HTTP_VAR_CHANGEABLE);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_userid_reset_variable;

    n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);

    if (n == NGX_ERROR) {

        return NGX_ERROR;

    }

    ngx_http_userid_reset_index = n;

    return NGX_OK;

}

static void *

‌ngx_http_userid_create_conf(ngx_conf_t *cf)

{

    ngx_http_userid_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->name = { 0, NULL };

     *     conf->domain = { 0, NULL };

     *     conf->path = { 0, NULL };

     *     conf->p3p = { 0, NULL };

     */

    conf->enable = NGX_CONF_UNSET_UINT;

    conf->service = NGX_CONF_UNSET;

    conf->expires = NGX_CONF_UNSET;

    conf->mark = (u_char) '\xFF';

    return conf;

}

static char *

‌ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_userid_conf_t *prev = parent;

    ngx_http_userid_conf_t *conf = child;

    ngx_conf_merge_uint_value(conf->enable, prev->enable,

                              NGX_HTTP_USERID_OFF);

    ngx_conf_merge_str_value(conf->name, prev->name, "uid");

    ngx_conf_merge_str_value(conf->domain, prev->domain, "");

    ngx_conf_merge_str_value(conf->path, prev->path, "; path=/");

    ngx_conf_merge_str_value(conf->p3p, prev->p3p, "");

    ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);

    ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);

    if (conf->mark == (u_char) '\xFF') {

        if (prev->mark == (u_char) '\xFF') {

            conf->mark = '\0';

        } else {

            conf->mark = prev->mark;

        }

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_userid_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_userid_filter;

    return NGX_OK;

}

static char *

‌ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data)

{

    ngx_str_t  *domain = data;

    u_char  *p, *new;

    if (ngx_strcmp(domain->data, "none") == 0) {

        ngx_str_set(domain, "");

        return NGX_CONF_OK;

    }

    new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len);

    if (new == NULL) {

        return NGX_CONF_ERROR;

    }

    p = ngx_cpymem(new, "; domain=", sizeof("; domain=") - 1);

    ngx_memcpy(p, domain->data, domain->len);

    domain->len += sizeof("; domain=") - 1;

    domain->data = new;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data)

{

    ngx_str_t  *path = data;

    u_char  *p, *new;

    new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len);

    if (new == NULL) {

        return NGX_CONF_ERROR;

    }

    p = ngx_cpymem(new, "; path=", sizeof("; path=") - 1);

    ngx_memcpy(p, path->data, path->len);

    path->len += sizeof("; path=") - 1;

    path->data = new;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_userid_conf_t *ucf = conf;

    ngx_str_t  *value;

    if (ucf->expires != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "max") == 0) {

        ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;

        return NGX_CONF_OK;

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        ucf->expires = 0;

        return NGX_CONF_OK;

    }

    ucf->expires = ngx_parse_time(&value[1], 1);

    if (ucf->expires == (time_t) NGX_ERROR) {

        return "invalid value";

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data)

{

    ngx_str_t  *p3p = data;

    if (ngx_strcmp(p3p->data, "none") == 0) {

        ngx_str_set(p3p, "");

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_userid_conf_t *ucf = conf;

    ngx_str_t  *value;

    if (ucf->mark != (u_char) '\xFF') {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        ucf->mark = '\0';

        return NGX_CONF_OK;

    }

    if (value[1].len != 1

        || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')

              || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')

              || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')

              || value[1].data[0] == '='))

    {

        return "value must be \"off\" or a single letter, digit or \"=\"";

    }

    ucf->mark = value[1].data[0];

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_userid_init_worker(ngx_cycle_t *cycle)

{

    struct timeval  tp;

    ngx_gettimeofday(&tp);

    /* use the most significant usec part that fits to 16 bits */

    start_value = ((tp.tv_usec / 20) << 16) | ngx_pid;

    return NGX_OK;

}

src/http/ngx_http_spdy_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_header_filter

ngx_http_spdy_filter_module

ngx_http_spdy_filter_module_ctx

Functions defined

ngx_http_spdy_data_frame_handler

ngx_http_spdy_filter_cleanup

ngx_http_spdy_filter_get_data_frame

ngx_http_spdy_filter_get_shadow

ngx_http_spdy_filter_init

ngx_http_spdy_filter_send

ngx_http_spdy_flow_control

ngx_http_spdy_handle_frame

ngx_http_spdy_handle_stream

ngx_http_spdy_header_filter

ngx_http_spdy_send_chain

ngx_http_spdy_syn_frame_handler

ngx_http_spdy_waiting_queue

Macros defined

ngx_http_spdy_nv_nsize

ngx_http_spdy_nv_vsize

ngx_http_spdy_nv_write_name

ngx_http_spdy_nv_write_nlen

ngx_http_spdy_nv_write_num

ngx_http_spdy_nv_write_val

ngx_http_spdy_nv_write_vlen

Source code

/*

* Copyright (C) Nginx, Inc.

* Copyright (C) Valentin V. Bartenev

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <nginx.h>

#include <ngx_http_spdy_module.h>

#include <zlib.h>

‌#define ngx_http_spdy_nv_nsize(h)  (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)

‌#define ngx_http_spdy_nv_vsize(h)  (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)

‌#define ngx_http_spdy_nv_write_num   ngx_spdy_frame_write_uint32

‌#define ngx_http_spdy_nv_write_nlen  ngx_spdy_frame_write_uint32

‌#define ngx_http_spdy_nv_write_vlen  ngx_spdy_frame_write_uint32

‌#define ngx_http_spdy_nv_write_name(p, h)                                     \

    ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)

‌#define ngx_http_spdy_nv_write_val(p, h)                                      \

    ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)

static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc,

    ngx_chain_t *in, off_t limit);

static ngx_inline ngx_int_t ngx_http_spdy_filter_send(

    ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);

static ngx_inline ngx_int_t ngx_http_spdy_flow_control(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);

static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream);

static ngx_chain_t *ngx_http_spdy_filter_get_shadow(

    ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);

static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(

    ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first,

    ngx_chain_t *last);

static ngx_int_t ngx_http_spdy_syn_frame_handler(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);

static ngx_int_t ngx_http_spdy_data_frame_handler(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);

static ngx_inline void ngx_http_spdy_handle_frame(

    ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);

static ngx_inline void ngx_http_spdy_handle_stream(

    ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);

static void ngx_http_spdy_filter_cleanup(void *data);

static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_spdy_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_spdy_filter_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 */

};

‌ngx_module_t  ngx_http_spdy_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_spdy_filter_module_ctx,      /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

static ngx_int_t

‌ngx_http_spdy_header_filter(ngx_http_request_t *r)

{

    int                           rc;

    size_t                        len;

    u_char                       *p, *buf, *last;

    ngx_buf_t                    *b;

    ngx_str_t                     host;

    ngx_uint_t                    i, j, count, port;

    ngx_chain_t                  *cl;

    ngx_list_part_t              *part, *pt;

    ngx_table_elt_t              *header, *h;

    ngx_connection_t             *c;

    ngx_http_cleanup_t           *cln;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_core_srv_conf_t     *cscf;

    ngx_http_spdy_stream_t       *stream;

    ngx_http_spdy_out_frame_t    *frame;

    ngx_http_spdy_connection_t   *sc;

    struct sockaddr_in           *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6          *sin6;

#endif

    u_char                        addr[NGX_SOCKADDR_STRLEN];

    if (!r->spdy_stream) {

        return ngx_http_next_header_filter(r);

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "spdy header filter");

    if (r->header_sent) {

        return NGX_OK;

    }

    r->header_sent = 1;

    if (r != r->main) {

        return NGX_OK;

    }

    c = r->connection;

    if (r->method == NGX_HTTP_HEAD) {

        r->header_only = 1;

    }

    switch (r->headers_out.status) {

    case NGX_HTTP_OK:

    case NGX_HTTP_PARTIAL_CONTENT:

        break;

    case NGX_HTTP_NOT_MODIFIED:

        r->header_only = 1;

        break;

    case NGX_HTTP_NO_CONTENT:

        r->header_only = 1;

        ngx_str_null(&r->headers_out.content_type);

        r->headers_out.content_length = NULL;

        r->headers_out.content_length_n = -1;

        /* fall through */

    default:

        r->headers_out.last_modified_time = -1;

        r->headers_out.last_modified = NULL;

    }

    len = NGX_SPDY_NV_NUM_SIZE

          + ngx_http_spdy_nv_nsize(":version")

          + ngx_http_spdy_nv_vsize("HTTP/1.1")

          + ngx_http_spdy_nv_nsize(":status")

          + (r->headers_out.status_line.len

             ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len

             : ngx_http_spdy_nv_vsize("418"));

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->headers_out.server == NULL) {

        len += ngx_http_spdy_nv_nsize("server");

        len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)

                                   : ngx_http_spdy_nv_vsize("nginx");

    }

    if (r->headers_out.date == NULL) {

        len += ngx_http_spdy_nv_nsize("date")

               + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");

    }

    if (r->headers_out.content_type.len) {

        len += ngx_http_spdy_nv_nsize("content-type")

               + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;

        if (r->headers_out.content_type_len == r->headers_out.content_type.len

            && r->headers_out.charset.len)

        {

            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;

        }

    }

    if (r->headers_out.content_length == NULL

        && r->headers_out.content_length_n >= 0)

    {

        len += ngx_http_spdy_nv_nsize("content-length")

               + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;

    }

    if (r->headers_out.last_modified == NULL

        && r->headers_out.last_modified_time != -1)

    {

        len += ngx_http_spdy_nv_nsize("last-modified")

               + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");

    }

    if (r->headers_out.location

        && r->headers_out.location->value.len

        && r->headers_out.location->value.data[0] == '/')

    {

        r->headers_out.location->hash = 0;

        if (clcf->server_name_in_redirect) {

            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

            host = cscf->server_name;

        } else if (r->headers_in.server.len) {

            host = r->headers_in.server;

        } else {

            host.len = NGX_SOCKADDR_STRLEN;

            host.data = addr;

            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {

                return NGX_ERROR;

            }

        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            port = ntohs(sin6->sin6_port);

            break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        case AF_UNIX:

            port = 0;

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) c->local_sockaddr;

            port = ntohs(sin->sin_port);

            break;

        }

        len += ngx_http_spdy_nv_nsize("location")

               + ngx_http_spdy_nv_vsize("https://")

               + host.len

               + r->headers_out.location->value.len;

        if (clcf->port_in_redirect) {

#if (NGX_HTTP_SSL)

            if (c->ssl)

                port = (port == 443) ? 0 : port;

            else

#endif

                port = (port == 80) ? 0 : port;

        } else {

            port = 0;

        }

        if (port) {

            len += sizeof(":65535") - 1;

        }

    } else {

        ngx_str_null(&host);

        port = 0;

    }

    part = &r->headers_out.headers.part;

    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0) {

            continue;

        }

        len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len

               + NGX_SPDY_NV_VLEN_SIZE  + header[i].value.len;

    }

    buf = ngx_alloc(len, r->pool->log);

    if (buf == NULL) {

        return NGX_ERROR;

    }

    last = buf + NGX_SPDY_NV_NUM_SIZE;

    last = ngx_http_spdy_nv_write_name(last, ":version");

    last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");

    last = ngx_http_spdy_nv_write_name(last, ":status");

    if (r->headers_out.status_line.len) {

        last = ngx_http_spdy_nv_write_vlen(last,

                                           r->headers_out.status_line.len);

        last = ngx_cpymem(last, r->headers_out.status_line.data,

                          r->headers_out.status_line.len);

    } else {

        last = ngx_http_spdy_nv_write_vlen(last, 3);

        last = ngx_sprintf(last, "%03ui", r->headers_out.status);

    }

    count = 2;

    if (r->headers_out.server == NULL) {

        last = ngx_http_spdy_nv_write_name(last, "server");

        last = clcf->server_tokens

               ? ngx_http_spdy_nv_write_val(last, NGINX_VER)

               : ngx_http_spdy_nv_write_val(last, "nginx");

        count++;

    }

    if (r->headers_out.date == NULL) {

        last = ngx_http_spdy_nv_write_name(last, "date");

        last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);

        last = ngx_cpymem(last, ngx_cached_http_time.data,

                          ngx_cached_http_time.len);

        count++;

    }

    if (r->headers_out.content_type.len) {

        last = ngx_http_spdy_nv_write_name(last, "content-type");

        p = last + NGX_SPDY_NV_VLEN_SIZE;

        last = ngx_cpymem(p, r->headers_out.content_type.data,

                          r->headers_out.content_type.len);

        if (r->headers_out.content_type_len == r->headers_out.content_type.len

            && r->headers_out.charset.len)

        {

            last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);

            last = ngx_cpymem(last, r->headers_out.charset.data,

                              r->headers_out.charset.len);

            /* update r->headers_out.content_type for possible logging */

            r->headers_out.content_type.len = last - p;

            r->headers_out.content_type.data = p;

        }

        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,

                                           r->headers_out.content_type.len);

        count++;

    }

    if (r->headers_out.content_length == NULL

        && r->headers_out.content_length_n >= 0)

    {

        last = ngx_http_spdy_nv_write_name(last, "content-length");

        p = last + NGX_SPDY_NV_VLEN_SIZE;

        last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);

        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,

                                           last - p);

        count++;

    }

    if (r->headers_out.last_modified == NULL

        && r->headers_out.last_modified_time != -1)

    {

        last = ngx_http_spdy_nv_write_name(last, "last-modified");

        p = last + NGX_SPDY_NV_VLEN_SIZE;

        last = ngx_http_time(p, r->headers_out.last_modified_time);

        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,

                                           last - p);

        count++;

    }

    if (host.data) {

        last = ngx_http_spdy_nv_write_name(last, "location");

        p = last + NGX_SPDY_NV_VLEN_SIZE;

        last = ngx_cpymem(p, "http", sizeof("http") - 1);

#if (NGX_HTTP_SSL)

        if (c->ssl) {

            *last++ ='s';

        }

#endif

        *last++ = ':'; *last++ = '/'; *last++ = '/';

        last = ngx_cpymem(last, host.data, host.len);

        if (port) {

            last = ngx_sprintf(last, ":%ui", port);

        }

        last = ngx_cpymem(last, r->headers_out.location->value.data,

                          r->headers_out.location->value.len);

        /* update r->headers_out.location->value for possible logging */

        r->headers_out.location->value.len = last - p;

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

        ngx_str_set(&r->headers_out.location->key, "location");

        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,

                                           r->headers_out.location->value.len);

        count++;

    }

    part = &r->headers_out.headers.part;

    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0 || header[i].hash == 2) {

            continue;

        }

        last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);

        ngx_strlow(last, header[i].key.data, header[i].key.len);

        last += header[i].key.len;

        p = last + NGX_SPDY_NV_VLEN_SIZE;

        last = ngx_cpymem(p, header[i].value.data, header[i].value.len);

        pt = part;

        h = header;

        for (j = i + 1; /* void */; j++) {

            if (j >= pt->nelts) {

                if (pt->next == NULL) {

                    break;

                }

                pt = pt->next;

                h = pt->elts;

                j = 0;

            }

            if (h[j].hash == 0 || h[j].hash == 2

                || h[j].key.len != header[i].key.len

                || ngx_strncasecmp(header[i].key.data, h[j].key.data,

                                   header[i].key.len))

            {

                continue;

            }

            if (h[j].value.len) {

                if (last != p) {

                    *last++ = '\0';

                }

                last = ngx_cpymem(last, h[j].value.data, h[j].value.len);

            }

            h[j].hash = 2;

        }

        (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,

                                           last - p);

        count++;

    }

    (void) ngx_http_spdy_nv_write_num(buf, count);

    stream = r->spdy_stream;

    sc = stream->connection;

    len = last - buf;

    b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE

                                     + NGX_SPDY_SYN_REPLY_SIZE

                                     + deflateBound(&sc->zstream_out, len));

    if (b == NULL) {

        ngx_free(buf);

        return NGX_ERROR;

    }

    b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;

    sc->zstream_out.next_in = buf;

    sc->zstream_out.avail_in = len;

    sc->zstream_out.next_out = b->last;

    sc->zstream_out.avail_out = b->end - b->last;

    rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);

    ngx_free(buf);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflate() failed: %d", rc);

        return NGX_ERROR;

    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                   sc->zstream_out.next_in, sc->zstream_out.next_out,

                   sc->zstream_out.avail_in, sc->zstream_out.avail_out,

                   rc);

    b->last = sc->zstream_out.next_out;

    p = b->pos;

    p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);

    len = b->last - b->pos;

    r->header_size = len;

    len -= NGX_SPDY_FRAME_HEADER_SIZE;

    if (r->header_only) {

        b->last_buf = 1;

        p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len);

    } else {

        p = ngx_spdy_frame_write_flags_and_len(p, 0, len);

    }

    (void) ngx_spdy_frame_write_sid(p, stream->id);

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    cl->next = NULL;

    frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));

    if (frame == NULL) {

        return NGX_ERROR;

    }

    frame->first = cl;

    frame->last = cl;

    frame->handler = ngx_http_spdy_syn_frame_handler;

    frame->stream = stream;

    frame->length = len;

    frame->priority = stream->priority;

    frame->blocked = 1;

    frame->fin = r->header_only;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,

                   "spdy:%ui create SYN_REPLY frame %p: len:%uz",

                   stream->id, frame, frame->length);

    ngx_http_spdy_queue_blocked_frame(sc, frame);

    cln = ngx_http_cleanup_add(r, 0);

    if (cln == NULL) {

        return NGX_ERROR;

    }

    cln->handler = ngx_http_spdy_filter_cleanup;

    cln->data = stream;

    stream->queued = 1;

    c->send_chain = ngx_http_spdy_send_chain;

    c->need_last_buf = 1;

    return ngx_http_spdy_filter_send(c, stream);

}

static ngx_chain_t *

‌ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)

{

    off_t                        size, offset;

    size_t                       rest, frame_size;

    ngx_chain_t                 *cl, *out, **ln;

    ngx_http_request_t          *r;

    ngx_http_spdy_stream_t      *stream;

    ngx_http_spdy_loc_conf_t    *slcf;

    ngx_http_spdy_out_frame_t   *frame;

    ngx_http_spdy_connection_t  *sc;

    r = fc->data;

    stream = r->spdy_stream;

#if (NGX_SUPPRESS_WARN)

    size = 0;

#endif

    while (in) {

        size = ngx_buf_size(in->buf);

        if (size || in->buf->last_buf) {

            break;

        }

        in = in->next;

    }

    if (in == NULL) {

        if (stream->queued) {

            fc->write->delayed = 1;

        } else {

            fc->buffered &= ~NGX_SPDY_BUFFERED;

        }

        return NULL;

    }

    sc = stream->connection;

    if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {

        fc->write->delayed = 1;

        return in;

    }

    if (limit == 0 || limit > (off_t) sc->send_window) {

        limit = sc->send_window;

    }

    if (limit > stream->send_window) {

        limit = (stream->send_window > 0) ? stream->send_window : 0;

    }

    if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_CHAIN_ERROR;

        }

        cl->buf = in->buf;

        in->buf = cl->buf->shadow;

        offset = ngx_buf_in_memory(in->buf)

                 ? (cl->buf->pos - in->buf->pos)

                 : (cl->buf->file_pos - in->buf->file_pos);

        cl->next = stream->free_bufs;

        stream->free_bufs = cl;

    } else {

        offset = 0;

    }

#if (NGX_SUPPRESS_WARN)

    cl = NULL;

#endif

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module);

    frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit

                                                     : slcf->chunk_size;

    for ( ;; ) {

        ln = &out;

        rest = frame_size;

        while ((off_t) rest >= size) {

            if (offset) {

                cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,

                                                     offset, size);

                if (cl == NULL) {

                    return NGX_CHAIN_ERROR;

                }

                offset = 0;

            } else {

                cl = ngx_alloc_chain_link(r->pool);

                if (cl == NULL) {

                    return NGX_CHAIN_ERROR;

                }

                cl->buf = in->buf;

            }

            *ln = cl;

            ln = &cl->next;

            rest -= (size_t) size;

            in = in->next;

            if (in == NULL) {

                frame_size -= rest;

                rest = 0;

                break;

            }

            size = ngx_buf_size(in->buf);

        }

        if (rest) {

            cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,

                                                 offset, rest);

            if (cl == NULL) {

                return NGX_CHAIN_ERROR;

            }

            cl->buf->flush = 0;

            cl->buf->last_buf = 0;

            *ln = cl;

            offset += rest;

            size -= rest;

        }

        frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size,

                                                    out, cl);

        if (frame == NULL) {

            return NGX_CHAIN_ERROR;

        }

        ngx_http_spdy_queue_frame(sc, frame);

        sc->send_window -= frame_size;

        stream->send_window -= frame_size;

        stream->queued++;

        if (in == NULL) {

            break;

        }

        limit -= frame_size;

        if (limit == 0) {

            break;

        }

        if (limit < (off_t) slcf->chunk_size) {

            frame_size = (size_t) limit;

        }

    }

    if (offset) {

        cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);

        if (cl == NULL) {

            return NGX_CHAIN_ERROR;

        }

        in->buf = cl->buf;

        ngx_free_chain(r->pool, cl);

    }

    if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {

        return NGX_CHAIN_ERROR;

    }

    if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {

        fc->write->delayed = 1;

    }

    return in;

}

static ngx_chain_t *

‌ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,

    off_t offset, off_t size)

{

    ngx_buf_t    *chunk;

    ngx_chain_t  *cl;

    cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);

    if (cl == NULL) {

        return NULL;

    }

    chunk = cl->buf;

    ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));

    chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow;

    chunk->shadow = buf;

    if (ngx_buf_in_memory(chunk)) {

        chunk->pos += offset;

        chunk->last = chunk->pos + size;

    }

    if (chunk->in_file) {

        chunk->file_pos += offset;

        chunk->file_last = chunk->file_pos + size;

    }

    return cl;

}

static ngx_http_spdy_out_frame_t *

‌ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,

    size_t len, ngx_chain_t *first, ngx_chain_t *last)

{

    u_char                     *p;

    ngx_buf_t                  *buf;

    ngx_uint_t                  flags;

    ngx_chain_t                *cl;

    ngx_http_spdy_out_frame_t  *frame;

    frame = stream->free_frames;

    if (frame) {

        stream->free_frames = frame->next;

    } else {

        frame = ngx_palloc(stream->request->pool,

                           sizeof(ngx_http_spdy_out_frame_t));

        if (frame == NULL) {

            return NULL;

        }

    }

    flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0;

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,

                   "spdy:%ui create DATA frame %p: len:%uz flags:%ui",

                   stream->id, frame, len, flags);

    cl = ngx_chain_get_free_buf(stream->request->pool,

                                &stream->free_data_headers);

    if (cl == NULL) {

        return NULL;

    }

    buf = cl->buf;

    if (buf->start) {

        p = buf->start;

        buf->pos = p;

        p += NGX_SPDY_SID_SIZE;

        (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);

    } else {

        p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);

        if (p == NULL) {

            return NULL;

        }

        buf->pos = p;

        buf->start = p;

        p = ngx_spdy_frame_write_sid(p, stream->id);

        p = ngx_spdy_frame_write_flags_and_len(p, flags, len);

        buf->last = p;

        buf->end = p;

        buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;

        buf->memory = 1;

    }

    cl->next = first;

    first = cl;

    last->buf->flush = 1;

    frame->first = first;

    frame->last = last;

    frame->handler = ngx_http_spdy_data_frame_handler;

    frame->stream = stream;

    frame->length = len;

    frame->priority = stream->priority;

    frame->blocked = 0;

    frame->fin = last->buf->last_buf;

    return frame;

}

static ngx_inline ngx_int_t

‌ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)

{

    stream->blocked = 1;

    if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {

        fc->error = 1;

        return NGX_ERROR;

    }

    stream->blocked = 0;

    if (stream->queued) {

        fc->buffered |= NGX_SPDY_BUFFERED;

        fc->write->delayed = 1;

        return NGX_AGAIN;

    }

    fc->buffered &= ~NGX_SPDY_BUFFERED;

    return NGX_OK;

}

static ngx_inline ngx_int_t

‌ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream)

{

    if (stream->send_window <= 0) {

        stream->exhausted = 1;

        return NGX_DECLINED;

    }

    if (sc->send_window == 0) {

        ngx_http_spdy_waiting_queue(sc, stream);

        return NGX_DECLINED;

    }

    return NGX_OK;

}

static void

‌ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream)

{

    ngx_queue_t             *q;

    ngx_http_spdy_stream_t  *s;

    if (stream->handled) {

        return;

    }

    stream->handled = 1;

    for (q = ngx_queue_last(&sc->waiting);

         q != ngx_queue_sentinel(&sc->waiting);

         q = ngx_queue_prev(q))

    {

        s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);

        /*

         * NB: higher values represent lower priorities.

         */

        if (stream->priority >= s->priority) {

            break;

        }

    }

    ngx_queue_insert_after(q, &stream->queue);

}

static ngx_int_t

‌ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_buf_t               *buf;

    ngx_http_spdy_stream_t  *stream;

    buf = frame->first->buf;

    if (buf->pos != buf->last) {

        return NGX_AGAIN;

    }

    stream = frame->stream;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);

    ngx_free_chain(stream->request->pool, frame->first);

    ngx_http_spdy_handle_frame(stream, frame);

    ngx_http_spdy_handle_stream(sc, stream);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_buf_t               *buf;

    ngx_chain_t             *cl, *ln;

    ngx_http_spdy_stream_t  *stream;

    stream = frame->stream;

    cl = frame->first;

    if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) {

        if (cl->buf->pos != cl->buf->last) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                           "spdy:%ui DATA frame %p was sent partially",

                           stream->id, frame);

            return NGX_AGAIN;

        }

        ln = cl->next;

        cl->next = stream->free_data_headers;

        stream->free_data_headers = cl;

        if (cl == frame->last) {

            goto done;

        }

        cl = ln;

    }

    for ( ;; ) {

        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {

            buf = cl->buf->shadow;

            if (ngx_buf_in_memory(buf)) {

                buf->pos = cl->buf->pos;

            }

            if (buf->in_file) {

                buf->file_pos = cl->buf->file_pos;

            }

        }

        if (ngx_buf_size(cl->buf) != 0) {

            if (cl != frame->first) {

                frame->first = cl;

                ngx_http_spdy_handle_stream(sc, stream);

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                           "spdy:%ui DATA frame %p was sent partially",

                           stream->id, frame);

            return NGX_AGAIN;

        }

        ln = cl->next;

        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {

            cl->next = stream->free_bufs;

            stream->free_bufs = cl;

        } else {

            ngx_free_chain(stream->request->pool, cl);

        }

        if (cl == frame->last) {

            goto done;

        }

        cl = ln;

    }

done:

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,

                   "spdy:%ui DATA frame %p was sent", stream->id, frame);

    stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;

    ngx_http_spdy_handle_frame(stream, frame);

    ngx_http_spdy_handle_stream(sc, stream);

    return NGX_OK;

}

static ngx_inline void

‌ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,

    ngx_http_spdy_out_frame_t *frame)

{

    ngx_http_request_t  *r;

    r = stream->request;

    r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length;

    if (frame->fin) {

        stream->out_closed = 1;

    }

    frame->next = stream->free_frames;

    stream->free_frames = frame;

    stream->queued--;

}

static ngx_inline void

‌ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,

    ngx_http_spdy_stream_t *stream)

{

    ngx_event_t  *wev;

    if (stream->handled || stream->blocked || stream->exhausted) {

        return;

    }

    wev = stream->request->connection->write;

    /*

     * This timer can only be set if the stream was delayed because of rate

     * limit.  In that case the event should be triggered by the timer.

     */

    if (!wev->timer_set) {

        wev->delayed = 0;

        stream->handled = 1;

        ngx_queue_insert_tail(&sc->posted, &stream->queue);

    }

}

static void

‌ngx_http_spdy_filter_cleanup(void *data)

{

    ngx_http_spdy_stream_t *stream = data;

    size_t                       delta;

    ngx_http_spdy_out_frame_t   *frame, **fn;

    ngx_http_spdy_connection_t  *sc;

    if (stream->handled) {

        stream->handled = 0;

        ngx_queue_remove(&stream->queue);

    }

    if (stream->queued == 0) {

        return;

    }

    delta = 0;

    sc = stream->connection;

    fn = &sc->last_out;

    for ( ;; ) {

        frame = *fn;

        if (frame == NULL) {

            break;

        }

        if (frame->stream == stream && !frame->blocked) {

            *fn = frame->next;

            delta += frame->length;

            if (--stream->queued == 0) {

                break;

            }

            continue;

        }

        fn = &frame->next;

    }

    if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) {

        ngx_queue_add(&sc->posted, &sc->waiting);

        ngx_queue_init(&sc->waiting);

    }

    sc->send_window += delta;

}

static ngx_int_t

‌ngx_http_spdy_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_spdy_header_filter;

    return NGX_OK;

}

src/http/ngx_http_spdy_module.h - nginx-1.7.9

Data types defined

ngx_http_spdy_loc_conf_t

ngx_http_spdy_main_conf_t

ngx_http_spdy_srv_conf_t

Macros defined

_NGX_HTTP_SPDY_MODULE_H_INCLUDED_

Source code

/*

* Copyright (C) Nginx, Inc.

* Copyright (C) Valentin V. Bartenev

*/

#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_

‌#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    size_t                          recv_buffer_size;

    u_char                         *recv_buffer;

‌} ngx_http_spdy_main_conf_t;

typedef struct {

    size_t                          pool_size;

    ngx_uint_t                      concurrent_streams;

    ngx_uint_t                      streams_index_mask;

    ngx_msec_t                      recv_timeout;

    ngx_msec_t                      keepalive_timeout;

    ngx_int_t                       headers_comp;

‌} ngx_http_spdy_srv_conf_t;

typedef struct {

    size_t                          chunk_size;

‌} ngx_http_spdy_loc_conf_t;

extern ngx_module_t  ngx_http_spdy_module;

#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */

src/http/ngx_http_write_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_write_filter_module

ngx_http_write_filter_module_ctx

Functions defined

ngx_http_write_filter

ngx_http_write_filter_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_write_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_write_filter_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 */

};

‌ngx_module_t  ngx_http_write_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_write_filter_module_ctx,     /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

ngx_int_t

‌ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    off_t                      size, sent, nsent, limit;

    ngx_uint_t                 last, flush, sync;

    ngx_msec_t                 delay;

    ngx_chain_t               *cl, *ln, **ll, *chain;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    if (c->error) {

        return NGX_ERROR;

    }

    size = 0;

    flush = 0;

    sync = 0;

    last = 0;

    ll = &r->out;

    /* find the size, the flush point and the last link of the saved chain */

    for (cl = r->out; cl; cl = cl->next) {

        ll = &cl->next;

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "write old buf t:%d f:%d %p, pos %p, size: %z "

                       "file: %O, size: %z",

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

#if 1

        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {

            ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                          "zero size buf in writer "

                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",

                          cl->buf->temporary,

                          cl->buf->recycled,

                          cl->buf->in_file,

                          cl->buf->start,

                          cl->buf->pos,

                          cl->buf->last,

                          cl->buf->file,

                          cl->buf->file_pos,

                          cl->buf->file_last);

            ngx_debug_point();

            return NGX_ERROR;

        }

#endif

        size += ngx_buf_size(cl->buf);

        if (cl->buf->flush || cl->buf->recycled) {

            flush = 1;

        }

        if (cl->buf->sync) {

            sync = 1;

        }

        if (cl->buf->last_buf) {

            last = 1;

        }

    }

    /* add the new chain to the existent one */

    for (ln = in; ln; ln = ln->next) {

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = ln->buf;

        *ll = cl;

        ll = &cl->next;

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "write new buf t:%d f:%d %p, pos %p, size: %z "

                       "file: %O, size: %z",

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

#if 1

        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {

            ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                          "zero size buf in writer "

                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",

                          cl->buf->temporary,

                          cl->buf->recycled,

                          cl->buf->in_file,

                          cl->buf->start,

                          cl->buf->pos,

                          cl->buf->last,

                          cl->buf->file,

                          cl->buf->file_pos,

                          cl->buf->file_last);

            ngx_debug_point();

            return NGX_ERROR;

        }

#endif

        size += ngx_buf_size(cl->buf);

        if (cl->buf->flush || cl->buf->recycled) {

            flush = 1;

        }

        if (cl->buf->sync) {

            sync = 1;

        }

        if (cl->buf->last_buf) {

            last = 1;

        }

    }

    *ll = NULL;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http write filter: l:%d f:%d s:%O", last, flush, size);

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    /*

     * avoid the output if there are no last buf, no flush point,

     * there are the incoming bufs and the size of all bufs

     * is smaller than "postpone_output" directive

     */

    if (!last && !flush && in && size < (off_t) clcf->postpone_output) {

        return NGX_OK;

    }

    if (c->write->delayed) {

        c->buffered |= NGX_HTTP_WRITE_BUFFERED;

        return NGX_AGAIN;

    }

    if (size == 0

        && !(c->buffered & NGX_LOWLEVEL_BUFFERED)

        && !(last && c->need_last_buf))

    {

        if (last || flush || sync) {

            for (cl = r->out; cl; /* void */) {

                ln = cl;

                cl = cl->next;

                ngx_free_chain(r->pool, ln);

            }

            r->out = NULL;

            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "the http output chain is empty");

        ngx_debug_point();

        return NGX_ERROR;

    }

    if (r->limit_rate) {

        if (r->limit_rate_after == 0) {

            r->limit_rate_after = clcf->limit_rate_after;

        }

        limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)

                - (c->sent - r->limit_rate_after);

        if (limit <= 0) {

            c->write->delayed = 1;

            delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);

            ngx_add_timer(c->write, delay);

            c->buffered |= NGX_HTTP_WRITE_BUFFERED;

            return NGX_AGAIN;

        }

        if (clcf->sendfile_max_chunk

            && (off_t) clcf->sendfile_max_chunk < limit)

        {

            limit = clcf->sendfile_max_chunk;

        }

    } else {

        limit = clcf->sendfile_max_chunk;

    }

    sent = c->sent;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http write filter limit %O", limit);

    chain = c->send_chain(c, r->out, limit);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http write filter %p", chain);

    if (chain == NGX_CHAIN_ERROR) {

        c->error = 1;

        return NGX_ERROR;

    }

    if (r->limit_rate) {

        nsent = c->sent;

        if (r->limit_rate_after) {

            sent -= r->limit_rate_after;

            if (sent < 0) {

                sent = 0;

            }

            nsent -= r->limit_rate_after;

            if (nsent < 0) {

                nsent = 0;

            }

        }

        delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);

        if (delay > 0) {

            limit = 0;

            c->write->delayed = 1;

            ngx_add_timer(c->write, delay);

        }

    }

    if (limit

        && c->write->ready

        && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))

    {

        c->write->delayed = 1;

        ngx_add_timer(c->write, 1);

    }

    for (cl = r->out; cl && cl != chain; /* void */) {

        ln = cl;

        cl = cl->next;

        ngx_free_chain(r->pool, ln);

    }

    r->out = chain;

    if (chain) {

        c->buffered |= NGX_HTTP_WRITE_BUFFERED;

        return NGX_AGAIN;

    }

    c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

    if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {

        return NGX_AGAIN;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_write_filter_init(ngx_conf_t *cf)

{

    ngx_http_top_body_filter = ngx_http_write_filter;

    return NGX_OK;

}

src/http/ngx_http_request_body.c - nginx-1.7.9

Functions defined

ngx_http_discard_request_body

ngx_http_discard_request_body_filter

ngx_http_discarded_request_body_handler

ngx_http_do_read_client_request_body

ngx_http_read_client_request_body

ngx_http_read_client_request_body_handler

ngx_http_read_discarded_request_body

ngx_http_request_body_chunked_filter

ngx_http_request_body_filter

ngx_http_request_body_length_filter

ngx_http_request_body_save_filter

ngx_http_test_expect

ngx_http_write_request_body

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);

static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);

static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);

static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,

    ngx_buf_t *b);

static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);

static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,

    ngx_chain_t *in);

static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,

    ngx_chain_t *in);

static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,

    ngx_chain_t *in);

static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,

    ngx_chain_t *in);

ngx_int_t

‌ngx_http_read_client_request_body(ngx_http_request_t *r,

    ngx_http_client_body_handler_pt post_handler)

{

    size_t                     preread;

    ssize_t                    size;

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_chain_t                out, *cl;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    r->main->count++;

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream && r == r->main) {

        rc = ngx_http_spdy_read_request_body(r, post_handler);

        goto done;

    }

#endif

    if (r != r->main || r->request_body || r->discard_body) {

        post_handler(r);

        return NGX_OK;

    }

    if (ngx_http_test_expect(r) != NGX_OK) {

        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        goto done;

    }

    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

    if (rb == NULL) {

        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        goto done;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     rb->bufs = NULL;

     *     rb->buf = NULL;

     *     rb->free = NULL;

     *     rb->busy = NULL;

     *     rb->chunked = NULL;

     */

    rb->rest = -1;

    rb->post_handler = post_handler;

    r->request_body = rb;

    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {

        post_handler(r);

        return NGX_OK;

    }

    preread = r->header_in->last - r->header_in->pos;

    if (preread) {

        /* there is the pre-read part of the request body */

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http client request body preread %uz", preread);

        out.buf = r->header_in;

        out.next = NULL;

        rc = ngx_http_request_body_filter(r, &out);

        if (rc != NGX_OK) {

            goto done;

        }

        r->request_length += preread - (r->header_in->last - r->header_in->pos);

        if (!r->headers_in.chunked

            && rb->rest > 0

            && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))

        {

            /* the whole request body may be placed in r->header_in */

            b = ngx_calloc_buf(r->pool);

            if (b == NULL) {

                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

                goto done;

            }

            b->temporary = 1;

            b->start = r->header_in->pos;

            b->pos = r->header_in->pos;

            b->last = r->header_in->last;

            b->end = r->header_in->end;

            rb->buf = b;

            r->read_event_handler = ngx_http_read_client_request_body_handler;

            r->write_event_handler = ngx_http_request_empty_handler;

            rc = ngx_http_do_read_client_request_body(r);

            goto done;

        }

    } else {

        /* set rb->rest */

        if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {

            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

            goto done;

        }

    }

    if (rb->rest == 0) {

        /* the whole request body was pre-read */

        if (r->request_body_in_file_only) {

            if (ngx_http_write_request_body(r) != NGX_OK) {

                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

                goto done;

            }

            if (rb->temp_file->file.offset != 0) {

                cl = ngx_chain_get_free_buf(r->pool, &rb->free);

                if (cl == NULL) {

                    rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

                    goto done;

                }

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->in_file = 1;

                b->file_last = rb->temp_file->file.offset;

                b->file = &rb->temp_file->file;

                rb->bufs = cl;

            } else {

                rb->bufs = NULL;

            }

        }

        post_handler(r);

        return NGX_OK;

    }

    if (rb->rest < 0) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "negative request body rest");

        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        goto done;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    size = clcf->client_body_buffer_size;

    size += size >> 2;

    /* TODO: honor r->request_body_in_single_buf */

    if (!r->headers_in.chunked && rb->rest < size) {

        size = (ssize_t) rb->rest;

        if (r->request_body_in_single_buf) {

            size += preread;

        }

    } else {

        size = clcf->client_body_buffer_size;

    }

    rb->buf = ngx_create_temp_buf(r->pool, size);

    if (rb->buf == NULL) {

        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        goto done;

    }

    r->read_event_handler = ngx_http_read_client_request_body_handler;

    r->write_event_handler = ngx_http_request_empty_handler;

    rc = ngx_http_do_read_client_request_body(r);

done:

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        r->main->count--;

    }

    return rc;

}

static void

‌ngx_http_read_client_request_body_handler(ngx_http_request_t *r)

{

    ngx_int_t  rc;

    if (r->connection->read->timedout) {

        r->connection->timedout = 1;

        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    rc = ngx_http_do_read_client_request_body(r);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        ngx_http_finalize_request(r, rc);

    }

}

static ngx_int_t

‌ngx_http_do_read_client_request_body(ngx_http_request_t *r)

{

    off_t                      rest;

    size_t                     size;

    ssize_t                    n;

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_chain_t               *cl, out;

    ngx_connection_t          *c;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    rb = r->request_body;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http read client request body");

    for ( ;; ) {

        for ( ;; ) {

            if (rb->buf->last == rb->buf->end) {

                /* pass buffer to request body filter chain */

                out.buf = rb->buf;

                out.next = NULL;

                rc = ngx_http_request_body_filter(r, &out);

                if (rc != NGX_OK) {

                    return rc;

                }

                /* write to file */

                if (ngx_http_write_request_body(r) != NGX_OK) {

                    return NGX_HTTP_INTERNAL_SERVER_ERROR;

                }

                /* update chains */

                rc = ngx_http_request_body_filter(r, NULL);

                if (rc != NGX_OK) {

                    return rc;

                }

                if (rb->busy != NULL) {

                    return NGX_HTTP_INTERNAL_SERVER_ERROR;

                }

                rb->buf->pos = rb->buf->start;

                rb->buf->last = rb->buf->start;

            }

            size = rb->buf->end - rb->buf->last;

            rest = rb->rest - (rb->buf->last - rb->buf->pos);

            if ((off_t) size > rest) {

                size = (size_t) rest;

            }

            n = c->recv(c, rb->buf->last, size);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http client request body recv %z", n);

            if (n == NGX_AGAIN) {

                break;

            }

            if (n == 0) {

                ngx_log_error(NGX_LOG_INFO, c->log, 0,

                              "client prematurely closed connection");

            }

            if (n == 0 || n == NGX_ERROR) {

                c->error = 1;

                return NGX_HTTP_BAD_REQUEST;

            }

            rb->buf->last += n;

            r->request_length += n;

            if (n == rest) {

                /* pass buffer to request body filter chain */

                out.buf = rb->buf;

                out.next = NULL;

                rc = ngx_http_request_body_filter(r, &out);

                if (rc != NGX_OK) {

                    return rc;

                }

            }

            if (rb->rest == 0) {

                break;

            }

            if (rb->buf->last < rb->buf->end) {

                break;

            }

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "http client request body rest %O", rb->rest);

        if (rb->rest == 0) {

            break;

        }

        if (!c->read->ready) {

            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

            ngx_add_timer(c->read, clcf->client_body_timeout);

            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

            return NGX_AGAIN;

        }

    }

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    if (rb->temp_file || r->request_body_in_file_only) {

        /* save the last part */

        if (ngx_http_write_request_body(r) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        if (rb->temp_file->file.offset != 0) {

            cl = ngx_chain_get_free_buf(r->pool, &rb->free);

            if (cl == NULL) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->in_file = 1;

            b->file_last = rb->temp_file->file.offset;

            b->file = &rb->temp_file->file;

            rb->bufs = cl;

        } else {

            rb->bufs = NULL;

        }

    }

    r->read_event_handler = ngx_http_block_reading;

    rb->post_handler(r);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_write_request_body(ngx_http_request_t *r)

{

    ssize_t                    n;

    ngx_chain_t               *cl;

    ngx_temp_file_t           *tf;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http write client request body, bufs %p", rb->bufs);

    if (rb->temp_file == NULL) {

        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

        if (tf == NULL) {

            return NGX_ERROR;

        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;

        tf->file.log = r->connection->log;

        tf->path = clcf->client_body_temp_path;

        tf->pool = r->pool;

        tf->warn = "a client request body is buffered to a temporary file";

        tf->log_level = r->request_body_file_log_level;

        tf->persistent = r->request_body_in_persistent_file;

        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {

            tf->access = 0660;

        }

        rb->temp_file = tf;

        if (rb->bufs == NULL) {

            /* empty body with r->request_body_in_file_only */

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,

                                     tf->persistent, tf->clean, tf->access)

                != NGX_OK)

            {

                return NGX_ERROR;

            }

            return NGX_OK;

        }

    }

    if (rb->bufs == NULL) {

        return NGX_OK;

    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {

        return NGX_ERROR;

    }

    rb->temp_file->offset += n;

    /* mark all buffers as written */

    for (cl = rb->bufs; cl; cl = cl->next) {

        cl->buf->pos = cl->buf->last;

    }

    rb->bufs = NULL;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_discard_request_body(ngx_http_request_t *r)

{

    ssize_t       size;

    ngx_int_t     rc;

    ngx_event_t  *rev;

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream && r == r->main) {

        r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;

        return NGX_OK;

    }

#endif

    if (r != r->main || r->discard_body || r->request_body) {

        return NGX_OK;

    }

    if (ngx_http_test_expect(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    rev = r->connection->read;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");

    if (rev->timer_set) {

        ngx_del_timer(rev);

    }

    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {

        return NGX_OK;

    }

    size = r->header_in->last - r->header_in->pos;

    if (size || r->headers_in.chunked) {

        rc = ngx_http_discard_request_body_filter(r, r->header_in);

        if (rc != NGX_OK) {

            return rc;

        }

        if (r->headers_in.content_length_n == 0) {

            return NGX_OK;

        }

    }

    rc = ngx_http_read_discarded_request_body(r);

    if (rc == NGX_OK) {

        r->lingering_close = 0;

        return NGX_OK;

    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        return rc;

    }

    /* rc == NGX_AGAIN */

    r->read_event_handler = ngx_http_discarded_request_body_handler;

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    r->count++;

    r->discard_body = 1;

    return NGX_OK;

}

void

‌ngx_http_discarded_request_body_handler(ngx_http_request_t *r)

{

    ngx_int_t                  rc;

    ngx_msec_t                 timer;

    ngx_event_t               *rev;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    rev = c->read;

    if (rev->timedout) {

        c->timedout = 1;

        c->error = 1;

        ngx_http_finalize_request(r, NGX_ERROR);

        return;

    }

    if (r->lingering_time) {

        timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();

        if ((ngx_msec_int_t) timer <= 0) {

            r->discard_body = 0;

            r->lingering_close = 0;

            ngx_http_finalize_request(r, NGX_ERROR);

            return;

        }

    } else {

        timer = 0;

    }

    rc = ngx_http_read_discarded_request_body(r);

    if (rc == NGX_OK) {

        r->discard_body = 0;

        r->lingering_close = 0;

        ngx_http_finalize_request(r, NGX_DONE);

        return;

    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        c->error = 1;

        ngx_http_finalize_request(r, NGX_ERROR);

        return;

    }

    /* rc == NGX_AGAIN */

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        c->error = 1;

        ngx_http_finalize_request(r, NGX_ERROR);

        return;

    }

    if (timer) {

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        timer *= 1000;

        if (timer > clcf->lingering_timeout) {

            timer = clcf->lingering_timeout;

        }

        ngx_add_timer(rev, timer);

    }

}

static ngx_int_t

‌ngx_http_read_discarded_request_body(ngx_http_request_t *r)

{

    size_t     size;

    ssize_t    n;

    ngx_int_t  rc;

    ngx_buf_t  b;

    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http read discarded body");

    ngx_memzero(&b, sizeof(ngx_buf_t));

    b.temporary = 1;

    for ( ;; ) {

        if (r->headers_in.content_length_n == 0) {

            r->read_event_handler = ngx_http_block_reading;

            return NGX_OK;

        }

        if (!r->connection->read->ready) {

            return NGX_AGAIN;

        }

        size = (size_t) ngx_min(r->headers_in.content_length_n,

                                NGX_HTTP_DISCARD_BUFFER_SIZE);

        n = r->connection->recv(r->connection, buffer, size);

        if (n == NGX_ERROR) {

            r->connection->error = 1;

            return NGX_OK;

        }

        if (n == NGX_AGAIN) {

            return NGX_AGAIN;

        }

        if (n == 0) {

            return NGX_OK;

        }

        b.pos = buffer;

        b.last = buffer + n;

        rc = ngx_http_discard_request_body_filter(r, &b);

        if (rc != NGX_OK) {

            return rc;

        }

    }

}

static ngx_int_t

‌ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)

{

    size_t                    size;

    ngx_int_t                 rc;

    ngx_http_request_body_t  *rb;

    if (r->headers_in.chunked) {

        rb = r->request_body;

        if (rb == NULL) {

            rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

            if (rb == NULL) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

            rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));

            if (rb->chunked == NULL) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

            r->request_body = rb;

        }

        for ( ;; ) {

            rc = ngx_http_parse_chunked(r, b, rb->chunked);

            if (rc == NGX_OK) {

                /* a chunk has been parsed successfully */

                size = b->last - b->pos;

                if ((off_t) size > rb->chunked->size) {

                    b->pos += (size_t) rb->chunked->size;

                    rb->chunked->size = 0;

                } else {

                    rb->chunked->size -= size;

                    b->pos = b->last;

                }

                continue;

            }

            if (rc == NGX_DONE) {

                /* a whole response has been parsed successfully */

                r->headers_in.content_length_n = 0;

                break;

            }

            if (rc == NGX_AGAIN) {

                /* set amount of data we want to see next time */

                r->headers_in.content_length_n = rb->chunked->length;

                break;

            }

            /* invalid */

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "client sent invalid chunked body");

            return NGX_HTTP_BAD_REQUEST;

        }

    } else {

        size = b->last - b->pos;

        if ((off_t) size > r->headers_in.content_length_n) {

            b->pos += (size_t) r->headers_in.content_length_n;

            r->headers_in.content_length_n = 0;

        } else {

            b->pos = b->last;

            r->headers_in.content_length_n -= size;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_test_expect(ngx_http_request_t *r)

{

    ngx_int_t   n;

    ngx_str_t  *expect;

    if (r->expect_tested

        || r->headers_in.expect == NULL

        || r->http_version < NGX_HTTP_VERSION_11)

    {

        return NGX_OK;

    }

    r->expect_tested = 1;

    expect = &r->headers_in.expect->value;

    if (expect->len != sizeof("100-continue") - 1

        || ngx_strncasecmp(expect->data, (u_char *) "100-continue",

                           sizeof("100-continue") - 1)

           != 0)

    {

        return NGX_OK;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "send 100 Continue");

    n = r->connection->send(r->connection,

                            (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,

                            sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);

    if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {

        return NGX_OK;

    }

    /* we assume that such small packet should be send successfully */

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    if (r->headers_in.chunked) {

        return ngx_http_request_body_chunked_filter(r, in);

    } else {

        return ngx_http_request_body_length_filter(r, in);

    }

}

static ngx_int_t

‌ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    size_t                     size;

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_chain_t               *cl, *tl, *out, **ll;

    ngx_http_request_body_t   *rb;

    rb = r->request_body;

    if (rb->rest == -1) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http request body content length filter");

        rb->rest = r->headers_in.content_length_n;

    }

    out = NULL;

    ll = &out;

    for (cl = in; cl; cl = cl->next) {

        if (rb->rest == 0) {

            break;

        }

        tl = ngx_chain_get_free_buf(r->pool, &rb->free);

        if (tl == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        b = tl->buf;

        ngx_memzero(b, sizeof(ngx_buf_t));

        b->temporary = 1;

        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;

        b->start = cl->buf->pos;

        b->pos = cl->buf->pos;

        b->last = cl->buf->last;

        b->end = cl->buf->end;

        size = cl->buf->last - cl->buf->pos;

        if ((off_t) size < rb->rest) {

            cl->buf->pos = cl->buf->last;

            rb->rest -= size;

        } else {

            cl->buf->pos += (size_t) rb->rest;

            rb->rest = 0;

            b->last = cl->buf->pos;

            b->last_buf = 1;

        }

        *ll = tl;

        ll = &tl->next;

    }

    rc = ngx_http_request_body_save_filter(r, out);

    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,

                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);

    return rc;

}

static ngx_int_t

‌ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    size_t                     size;

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_chain_t               *cl, *out, *tl, **ll;

    ngx_http_request_body_t   *rb;

    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    if (rb->rest == -1) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http request body chunked filter");

        rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));

        if (rb->chunked == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        r->headers_in.content_length_n = 0;

        rb->rest = 3;

    }

    out = NULL;

    ll = &out;

    for (cl = in; cl; cl = cl->next) {

        for ( ;; ) {

            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,

                           "http body chunked buf "

                           "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",

                           cl->buf->temporary, cl->buf->in_file,

                           cl->buf->start, cl->buf->pos,

                           cl->buf->last - cl->buf->pos,

                           cl->buf->file_pos,

                           cl->buf->file_last - cl->buf->file_pos);

            rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);

            if (rc == NGX_OK) {

                /* a chunk has been parsed successfully */

                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

                if (clcf->client_max_body_size

                    && clcf->client_max_body_size

                       - r->headers_in.content_length_n < rb->chunked->size)

                {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "client intended to send too large chunked "

                                  "body: %O+%O bytes",

                                  r->headers_in.content_length_n,

                                  rb->chunked->size);

                    r->lingering_close = 1;

                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;

                }

                tl = ngx_chain_get_free_buf(r->pool, &rb->free);

                if (tl == NULL) {

                    return NGX_HTTP_INTERNAL_SERVER_ERROR;

                }

                b = tl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->temporary = 1;

                b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;

                b->start = cl->buf->pos;

                b->pos = cl->buf->pos;

                b->last = cl->buf->last;

                b->end = cl->buf->end;

                *ll = tl;

                ll = &tl->next;

                size = cl->buf->last - cl->buf->pos;

                if ((off_t) size > rb->chunked->size) {

                    cl->buf->pos += (size_t) rb->chunked->size;

                    r->headers_in.content_length_n += rb->chunked->size;

                    rb->chunked->size = 0;

                } else {

                    rb->chunked->size -= size;

                    r->headers_in.content_length_n += size;

                    cl->buf->pos = cl->buf->last;

                }

                b->last = cl->buf->pos;

                continue;

            }

            if (rc == NGX_DONE) {

                /* a whole response has been parsed successfully */

                rb->rest = 0;

                tl = ngx_chain_get_free_buf(r->pool, &rb->free);

                if (tl == NULL) {

                    return NGX_HTTP_INTERNAL_SERVER_ERROR;

                }

                b = tl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->last_buf = 1;

                *ll = tl;

                ll = &tl->next;

                break;

            }

            if (rc == NGX_AGAIN) {

                /* set rb->rest, amount of data we want to see next time */

                rb->rest = rb->chunked->length;

                break;

            }

            /* invalid */

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "client sent invalid chunked body");

            return NGX_HTTP_BAD_REQUEST;

        }

    }

    rc = ngx_http_request_body_save_filter(r, out);

    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,

                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);

    return rc;

}

static ngx_int_t

‌ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

#if (NGX_DEBUG)

    ngx_chain_t               *cl;

#endif

    ngx_http_request_body_t   *rb;

    rb = r->request_body;

#if (NGX_DEBUG)

    for (cl = rb->bufs; cl; cl = cl->next) {

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,

                       "http body old buf t:%d f:%d %p, pos %p, size: %z "

                       "file: %O, size: %z",

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

    for (cl = in; cl; cl = cl->next) {

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,

                       "http body new buf t:%d f:%d %p, pos %p, size: %z "

                       "file: %O, size: %z",

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

#endif

    /* TODO: coalesce neighbouring buffers */

    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    return NGX_OK;

}

src/http/ngx_http_script.c - nginx-1.7.9

Global variables defined

ngx_http_script_exit_code

Functions defined

ngx_http_compile_complex_value

ngx_http_complex_value

ngx_http_script_add_args_code

ngx_http_script_add_capture_code

ngx_http_script_add_code

ngx_http_script_add_copy_code

ngx_http_script_add_full_name_code

ngx_http_script_add_var_code

ngx_http_script_break_code

ngx_http_script_compile

ngx_http_script_complex_value_code

ngx_http_script_copy_capture_code

ngx_http_script_copy_capture_len_code

ngx_http_script_copy_code

ngx_http_script_copy_len_code

ngx_http_script_copy_var_code

ngx_http_script_copy_var_len_code

ngx_http_script_done

ngx_http_script_equal_code

ngx_http_script_file_code

ngx_http_script_flush_complex_value

ngx_http_script_flush_no_cacheable_variables

ngx_http_script_full_name_code

ngx_http_script_full_name_len_code

ngx_http_script_if_code

ngx_http_script_init_arrays

ngx_http_script_mark_args_code

ngx_http_script_nop_code

ngx_http_script_not_equal_code

ngx_http_script_regex_end_code

ngx_http_script_regex_start_code

ngx_http_script_return_code

ngx_http_script_run

ngx_http_script_set_var_code

ngx_http_script_start_args_code

ngx_http_script_start_code

ngx_http_script_value_code

ngx_http_script_var_code

ngx_http_script_var_set_handler_code

ngx_http_script_variables_count

ngx_http_set_complex_value_slot

ngx_http_set_predicate_slot

ngx_http_test_predicates

Macros defined

ngx_http_script_exit

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc);

static ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc);

static ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc,

    ngx_str_t *value, ngx_uint_t last);

static ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc,

    ngx_str_t *name);

static ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc);

#if (NGX_PCRE)

static ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc,

     ngx_uint_t n);

#endif

static ngx_int_t

     ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc);

static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e);

static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e);

‌#define ngx_http_script_exit  (u_char *) &ngx_http_script_exit_code

‌static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;

void

‌ngx_http_script_flush_complex_value(ngx_http_request_t *r,

    ngx_http_complex_value_t *val)

{

    ngx_uint_t *index;

    index = val->flushes;

    if (index) {

        while (*index != (ngx_uint_t) -1) {

            if (r->variables[*index].no_cacheable) {

                r->variables[*index].valid = 0;

                r->variables[*index].not_found = 0;

            }

            index++;

        }

    }

}

ngx_int_t

‌ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,

    ngx_str_t *value)

{

    size_t                        len;

    ngx_http_script_code_pt       code;

    ngx_http_script_len_code_pt   lcode;

    ngx_http_script_engine_t      e;

    if (val->lengths == NULL) {

        *value = val->value;

        return NGX_OK;

    }

    ngx_http_script_flush_complex_value(r, val);

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = val->lengths;

    e.request = r;

    e.flushed = 1;

    len = 0;

    while (*(uintptr_t *) e.ip) {

        lcode = *(ngx_http_script_len_code_pt *) e.ip;

        len += lcode(&e);

    }

    value->len = len;

    value->data = ngx_pnalloc(r->pool, len);

    if (value->data == NULL) {

        return NGX_ERROR;

    }

    e.ip = val->values;

    e.pos = value->data;

    e.buf = *value;

    while (*(uintptr_t *) e.ip) {

        code = *(ngx_http_script_code_pt *) e.ip;

        code((ngx_http_script_engine_t *) &e);

    }

    *value = e.buf;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)

{

    ngx_str_t                  *v;

    ngx_uint_t                  i, n, nv, nc;

    ngx_array_t                 flushes, lengths, values, *pf, *pl, *pv;

    ngx_http_script_compile_t   sc;

    v = ccv->value;

    nv = 0;

    nc = 0;

    for (i = 0; i < v->len; i++) {

        if (v->data[i] == '$') {

            if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {

                nc++;

            } else {

                nv++;

            }

        }

    }

    if ((v->len == 0 || v->data[0] != '$')

        && (ccv->conf_prefix || ccv->root_prefix))

    {

        if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {

            return NGX_ERROR;

        }

        ccv->conf_prefix = 0;

        ccv->root_prefix = 0;

    }

    ccv->complex_value->value = *v;

    ccv->complex_value->flushes = NULL;

    ccv->complex_value->lengths = NULL;

    ccv->complex_value->values = NULL;

    if (nv == 0 && nc == 0) {

        return NGX_OK;

    }

    n = nv + 1;

    if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    n = nv * (2 * sizeof(ngx_http_script_copy_code_t)

                  + sizeof(ngx_http_script_var_code_t))

        + sizeof(uintptr_t);

    if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    n = (nv * (2 * sizeof(ngx_http_script_copy_code_t)

                   + sizeof(ngx_http_script_var_code_t))

                + sizeof(uintptr_t)

                + v->len

                + sizeof(uintptr_t) - 1)

            & ~(sizeof(uintptr_t) - 1);

    if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    pf = &flushes;

    pl = &lengths;

    pv = &values;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = ccv->cf;

    sc.source = v;

    sc.flushes = &pf;

    sc.lengths = &pl;

    sc.values = &pv;

    sc.complete_lengths = 1;

    sc.complete_values = 1;

    sc.zero = ccv->zero;

    sc.conf_prefix = ccv->conf_prefix;

    sc.root_prefix = ccv->root_prefix;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_ERROR;

    }

    if (flushes.nelts) {

        ccv->complex_value->flushes = flushes.elts;

        ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;

    }

    ccv->complex_value->lengths = lengths.elts;

    ccv->complex_value->values = values.elts;

    return NGX_OK;

}

char *

‌ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t                          *value;

    ngx_http_complex_value_t          **cv;

    ngx_http_compile_complex_value_t    ccv;

    cv = (ngx_http_complex_value_t **) (p + cmd->offset);

    if (*cv != NULL) {

        return "duplicate";

    }

    *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

    if (*cv == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = *cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

ngx_int_t

‌ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)

{

    ngx_str_t                  val;

    ngx_uint_t                 i;

    ngx_http_complex_value_t  *cv;

    if (predicates == NULL) {

        return NGX_OK;

    }

    cv = predicates->elts;

    for (i = 0; i < predicates->nelts; i++) {

        if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {

            return NGX_ERROR;

        }

        if (val.len && (val.len != 1 || val.data[0] != '0')) {

            return NGX_DECLINED;

        }

    }

    return NGX_OK;

}

char *

‌ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_str_t                          *value;

    ngx_uint_t                          i;

    ngx_array_t                       **a;

    ngx_http_complex_value_t           *cv;

    ngx_http_compile_complex_value_t    ccv;

    a = (ngx_array_t **) (p + cmd->offset);

    if (*a == NGX_CONF_UNSET_PTR) {

        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t));

        if (*a == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        cv = ngx_array_push(*a);

        if (cv == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

        ccv.cf = cf;

        ccv.value = &value[i];

        ccv.complex_value = cv;

        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

ngx_uint_t

‌ngx_http_script_variables_count(ngx_str_t *value)

{

    ngx_uint_t  i, n;

    for (n = 0, i = 0; i < value->len; i++) {

        if (value->data[i] == '$') {

            n++;

        }

    }

    return n;

}

ngx_int_t

‌ngx_http_script_compile(ngx_http_script_compile_t *sc)

{

    u_char       ch;

    ngx_str_t    name;

    ngx_uint_t   i, bracket;

    if (ngx_http_script_init_arrays(sc) != NGX_OK) {

        return NGX_ERROR;

    }

    for (i = 0; i < sc->source->len; /* void */ ) {

        name.len = 0;

        if (sc->source->data[i] == '$') {

            if (++i == sc->source->len) {

                goto invalid_variable;

            }

#if (NGX_PCRE)

            {

            ngx_uint_t  n;

            if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {

                n = sc->source->data[i] - '0';

                if (sc->captures_mask & (1 << n)) {

                    sc->dup_capture = 1;

                }

                sc->captures_mask |= 1 << n;

                if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {

                    return NGX_ERROR;

                }

                i++;

                continue;

            }

            }

#endif

            if (sc->source->data[i] == '{') {

                bracket = 1;

                if (++i == sc->source->len) {

                    goto invalid_variable;

                }

                name.data = &sc->source->data[i];

            } else {

                bracket = 0;

                name.data = &sc->source->data[i];

            }

            for ( /* void */ ; i < sc->source->len; i++, name.len++) {

                ch = sc->source->data[i];

                if (ch == '}' && bracket) {

                    i++;

                    bracket = 0;

                    break;

                }

                if ((ch >= 'A' && ch <= 'Z')

                    || (ch >= 'a' && ch <= 'z')

                    || (ch >= '0' && ch <= '9')

                    || ch == '_')

                {

                    continue;

                }

                break;

            }

            if (bracket) {

                ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,

                                   "the closing bracket in \"%V\" "

                                   "variable is missing", &name);

                return NGX_ERROR;

            }

            if (name.len == 0) {

                goto invalid_variable;

            }

            sc->variables++;

            if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {

                return NGX_ERROR;

            }

            continue;

        }

        if (sc->source->data[i] == '?' && sc->compile_args) {

            sc->args = 1;

            sc->compile_args = 0;

            if (ngx_http_script_add_args_code(sc) != NGX_OK) {

                return NGX_ERROR;

            }

            i++;

            continue;

        }

        name.data = &sc->source->data[i];

        while (i < sc->source->len) {

            if (sc->source->data[i] == '$') {

                break;

            }

            if (sc->source->data[i] == '?') {

                sc->args = 1;

                if (sc->compile_args) {

                    break;

                }

            }

            i++;

            name.len++;

        }

        sc->size += name.len;

        if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    return ngx_http_script_done(sc);

invalid_variable:

    ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");

    return NGX_ERROR;

}

u_char *

‌ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,

    void *code_lengths, size_t len, void *code_values)

{

    ngx_uint_t                    i;

    ngx_http_script_code_pt       code;

    ngx_http_script_len_code_pt   lcode;

    ngx_http_script_engine_t      e;

    ngx_http_core_main_conf_t    *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    for (i = 0; i < cmcf->variables.nelts; i++) {

        if (r->variables[i].no_cacheable) {

            r->variables[i].valid = 0;

            r->variables[i].not_found = 0;

        }

    }

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = code_lengths;

    e.request = r;

    e.flushed = 1;

    while (*(uintptr_t *) e.ip) {

        lcode = *(ngx_http_script_len_code_pt *) e.ip;

        len += lcode(&e);

    }

    value->len = len;

    value->data = ngx_pnalloc(r->pool, len);

    if (value->data == NULL) {

        return NULL;

    }

    e.ip = code_values;

    e.pos = value->data;

    while (*(uintptr_t *) e.ip) {

        code = *(ngx_http_script_code_pt *) e.ip;

        code((ngx_http_script_engine_t *) &e);

    }

    return e.pos;

}

void

‌ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,

    ngx_array_t *indices)

{

    ngx_uint_t  n, *index;

    if (indices) {

        index = indices->elts;

        for (n = 0; n < indices->nelts; n++) {

            if (r->variables[index[n]].no_cacheable) {

                r->variables[index[n]].valid = 0;

                r->variables[index[n]].not_found = 0;

            }

        }

    }

}

static ngx_int_t

‌ngx_http_script_init_arrays(ngx_http_script_compile_t *sc)

{

    ngx_uint_t   n;

    if (sc->flushes && *sc->flushes == NULL) {

        n = sc->variables ? sc->variables : 1;

        *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));

        if (*sc->flushes == NULL) {

            return NGX_ERROR;

        }

    }

    if (*sc->lengths == NULL) {

        n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)

                             + sizeof(ngx_http_script_var_code_t))

            + sizeof(uintptr_t);

        *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);

        if (*sc->lengths == NULL) {

            return NGX_ERROR;

        }

    }

    if (*sc->values == NULL) {

        n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)

                              + sizeof(ngx_http_script_var_code_t))

                + sizeof(uintptr_t)

                + sc->source->len

                + sizeof(uintptr_t) - 1)

            & ~(sizeof(uintptr_t) - 1);

        *sc->values = ngx_array_create(sc->cf->pool, n, 1);

        if (*sc->values == NULL) {

            return NGX_ERROR;

        }

    }

    sc->variables = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_script_done(ngx_http_script_compile_t *sc)

{

    ngx_str_t    zero;

    uintptr_t   *code;

    if (sc->zero) {

        zero.len = 1;

        zero.data = (u_char *) "\0";

        if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    if (sc->conf_prefix || sc->root_prefix) {

        if (ngx_http_script_add_full_name_code(sc) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    if (sc->complete_lengths) {

        code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    if (sc->complete_values) {

        code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),

                                        &sc->main);

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    return NGX_OK;

}

void *

‌ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)

{

    if (*codes == NULL) {

        *codes = ngx_array_create(pool, 256, 1);

        if (*codes == NULL) {

            return NULL;

        }

    }

    return ngx_array_push_n(*codes, size);

}

void *

‌ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)

{

    u_char  *elts, **p;

    void    *new;

    elts = codes->elts;

    new = ngx_array_push_n(codes, size);

    if (new == NULL) {

        return NULL;

    }

    if (code) {

        if (elts != codes->elts) {

            p = code;

            *p += (u_char *) codes->elts - elts;

        }

    }

    return new;

}

static ngx_int_t

‌ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value,

    ngx_uint_t last)

{

    u_char                       *p;

    size_t                        size, len, zero;

    ngx_http_script_copy_code_t  *code;

    zero = (sc->zero && last);

    len = value->len + zero;

    code = ngx_http_script_add_code(*sc->lengths,

                                    sizeof(ngx_http_script_copy_code_t), NULL);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

    code->len = len;

    size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)

            & ~(sizeof(uintptr_t) - 1);

    code = ngx_http_script_add_code(*sc->values, size, &sc->main);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = ngx_http_script_copy_code;

    code->len = len;

    p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t),

                   value->data, value->len);

    if (zero) {

        *p = '\0';

        sc->zero = 0;

    }

    return NGX_OK;

}

size_t

‌ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_copy_code_t  *code;

    code = (ngx_http_script_copy_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_copy_code_t);

    return code->len;

}

void

‌ngx_http_script_copy_code(ngx_http_script_engine_t *e)

{

    u_char                       *p;

    ngx_http_script_copy_code_t  *code;

    code = (ngx_http_script_copy_code_t *) e->ip;

    p = e->pos;

    if (!e->skip) {

        e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),

                          code->len);

    }

    e->ip += sizeof(ngx_http_script_copy_code_t)

          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script copy: \"%*s\"", e->pos - p, p);

}

static ngx_int_t

‌ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)

{

    ngx_int_t                    index, *p;

    ngx_http_script_var_code_t  *code;

    index = ngx_http_get_variable_index(sc->cf, name);

    if (index == NGX_ERROR) {

        return NGX_ERROR;

    }

    if (sc->flushes) {

        p = ngx_array_push(*sc->flushes);

        if (p == NULL) {

            return NGX_ERROR;

        }

        *p = index;

    }

    code = ngx_http_script_add_code(*sc->lengths,

                                    sizeof(ngx_http_script_var_code_t), NULL);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;

    code->index = (uintptr_t) index;

    code = ngx_http_script_add_code(*sc->values,

                                    sizeof(ngx_http_script_var_code_t),

                                    &sc->main);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = ngx_http_script_copy_var_code;

    code->index = (uintptr_t) index;

    return NGX_OK;

}

size_t

‌ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)

{

    ngx_http_variable_value_t   *value;

    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    if (e->flushed) {

        value = ngx_http_get_indexed_variable(e->request, code->index);

    } else {

        value = ngx_http_get_flushed_variable(e->request, code->index);

    }

    if (value && !value->not_found) {

        return value->len;

    }

    return 0;

}

void

‌ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)

{

    u_char                      *p;

    ngx_http_variable_value_t   *value;

    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    if (!e->skip) {

        if (e->flushed) {

            value = ngx_http_get_indexed_variable(e->request, code->index);

        } else {

            value = ngx_http_get_flushed_variable(e->request, code->index);

        }

        if (value && !value->not_found) {

            p = e->pos;

            e->pos = ngx_copy(p, value->data, value->len);

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP,

                           e->request->connection->log, 0,

                           "http script var: \"%*s\"", e->pos - p, p);

        }

    }

}

static ngx_int_t

‌ngx_http_script_add_args_code(ngx_http_script_compile_t *sc)

{

    uintptr_t   *code;

    code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) ngx_http_script_mark_args_code;

    code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main);

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) ngx_http_script_start_args_code;

    return NGX_OK;

}

size_t

‌ngx_http_script_mark_args_code(ngx_http_script_engine_t *e)

{

    e->is_args = 1;

    e->ip += sizeof(uintptr_t);

    return 1;

}

void

‌ngx_http_script_start_args_code(ngx_http_script_engine_t *e)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script args");

    e->is_args = 1;

    e->args = e->pos;

    e->ip += sizeof(uintptr_t);

}

#if (NGX_PCRE)

void

‌ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)

{

    size_t                         len;

    ngx_int_t                      rc;

    ngx_uint_t                     n;

    ngx_http_request_t            *r;

    ngx_http_script_engine_t       le;

    ngx_http_script_len_code_pt    lcode;

    ngx_http_script_regex_code_t  *code;

    code = (ngx_http_script_regex_code_t *) e->ip;

    r = e->request;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http script regex: \"%V\"", &code->name);

    if (code->uri) {

        e->line = r->uri;

    } else {

        e->sp--;

        e->line.len = e->sp->len;

        e->line.data = e->sp->data;

    }

    rc = ngx_http_regex_exec(r, code->regex, &e->line);

    if (rc == NGX_DECLINED) {

        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {

            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                          "\"%V\" does not match \"%V\"",

                          &code->name, &e->line);

        }

        r->ncaptures = 0;

        if (code->test) {

            if (code->negative_test) {

                e->sp->len = 1;

                e->sp->data = (u_char *) "1";

            } else {

                e->sp->len = 0;

                e->sp->data = (u_char *) "";

            }

            e->sp++;

            e->ip += sizeof(ngx_http_script_regex_code_t);

            return;

        }

        e->ip += code->next;

        return;

    }

    if (rc == NGX_ERROR) {

        e->ip = ngx_http_script_exit;

        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

        return;

    }

    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {

        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                      "\"%V\" matches \"%V\"", &code->name, &e->line);

    }

    if (code->test) {

        if (code->negative_test) {

            e->sp->len = 0;

            e->sp->data = (u_char *) "";

        } else {

            e->sp->len = 1;

            e->sp->data = (u_char *) "1";

        }

        e->sp++;

        e->ip += sizeof(ngx_http_script_regex_code_t);

        return;

    }

    if (code->status) {

        e->status = code->status;

        if (!code->redirect) {

            e->ip = ngx_http_script_exit;

            return;

        }

    }

    if (code->uri) {

        r->internal = 1;

        r->valid_unparsed_uri = 0;

        if (code->break_cycle) {

            r->valid_location = 0;

            r->uri_changed = 0;

        } else {

            r->uri_changed = 1;

        }

    }

    if (code->lengths == NULL) {

        e->buf.len = code->size;

        if (code->uri) {

            if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {

                e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,

                                                 NGX_ESCAPE_ARGS);

            }

        }

        for (n = 2; n < r->ncaptures; n += 2) {

            e->buf.len += r->captures[n + 1] - r->captures[n];

        }

    } else {

        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

        le.ip = code->lengths->elts;

        le.line = e->line;

        le.request = r;

        le.quote = code->redirect;

        len = 0;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            len += lcode(&le);

        }

        e->buf.len = len;

    }

    if (code->add_args && r->args.len) {

        e->buf.len += r->args.len + 1;

    }

    e->buf.data = ngx_pnalloc(r->pool, e->buf.len);

    if (e->buf.data == NULL) {

        e->ip = ngx_http_script_exit;

        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

        return;

    }

    e->quote = code->redirect;

    e->pos = e->buf.data;

    e->ip += sizeof(ngx_http_script_regex_code_t);

}

void

‌ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)

{

    u_char                            *dst, *src;

    ngx_http_request_t                *r;

    ngx_http_script_regex_end_code_t  *code;

    code = (ngx_http_script_regex_end_code_t *) e->ip;

    r = e->request;

    e->quote = 0;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http script regex end");

    if (code->redirect) {

        dst = e->buf.data;

        src = e->buf.data;

        ngx_unescape_uri(&dst, &src, e->pos - e->buf.data,

                         NGX_UNESCAPE_REDIRECT);

        if (src < e->pos) {

            dst = ngx_movemem(dst, src, e->pos - src);

        }

        e->pos = dst;

        if (code->add_args && r->args.len) {

            *e->pos++ = (u_char) (code->args ? '&' : '?');

            e->pos = ngx_copy(e->pos, r->args.data, r->args.len);

        }

        e->buf.len = e->pos - e->buf.data;

        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {

            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                          "rewritten redirect: \"%V\"", &e->buf);

        }

        ngx_http_clear_location(r);

        r->headers_out.location = ngx_list_push(&r->headers_out.headers);

        if (r->headers_out.location == NULL) {

            e->ip = ngx_http_script_exit;

            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

            return;

        }

        r->headers_out.location->hash = 1;

        ngx_str_set(&r->headers_out.location->key, "Location");

        r->headers_out.location->value = e->buf;

        e->ip += sizeof(ngx_http_script_regex_end_code_t);

        return;

    }

    if (e->args) {

        e->buf.len = e->args - e->buf.data;

        if (code->add_args && r->args.len) {

            *e->pos++ = '&';

            e->pos = ngx_copy(e->pos, r->args.data, r->args.len);

        }

        r->args.len = e->pos - e->args;

        r->args.data = e->args;

        e->args = NULL;

    } else {

        e->buf.len = e->pos - e->buf.data;

        if (!code->add_args) {

            r->args.len = 0;

        }

    }

    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {

        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                      "rewritten data: \"%V\", args: \"%V\"",

                      &e->buf, &r->args);

    }

    if (code->uri) {

        r->uri = e->buf;

        if (r->uri.len == 0) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "the rewritten URI has a zero length");

            e->ip = ngx_http_script_exit;

            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

            return;

        }

        ngx_http_set_exten(r);

    }

    e->ip += sizeof(ngx_http_script_regex_end_code_t);

}

static ngx_int_t

‌ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n)

{

    ngx_http_script_copy_capture_code_t  *code;

    code = ngx_http_script_add_code(*sc->lengths,

                                    sizeof(ngx_http_script_copy_capture_code_t),

                                    NULL);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = (ngx_http_script_code_pt)

                      ngx_http_script_copy_capture_len_code;

    code->n = 2 * n;

    code = ngx_http_script_add_code(*sc->values,

                                    sizeof(ngx_http_script_copy_capture_code_t),

                                    &sc->main);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = ngx_http_script_copy_capture_code;

    code->n = 2 * n;

    if (sc->ncaptures < n) {

        sc->ncaptures = n;

    }

    return NGX_OK;

}

size_t

‌ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)

{

    int                                  *cap;

    u_char                               *p;

    ngx_uint_t                            n;

    ngx_http_request_t                   *r;

    ngx_http_script_copy_capture_code_t  *code;

    r = e->request;

    code = (ngx_http_script_copy_capture_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_copy_capture_code_t);

    n = code->n;

    if (n < r->ncaptures) {

        cap = r->captures;

        if ((e->is_args || e->quote)

            && (e->request->quoted_uri || e->request->plus_in_uri))

        {

            p = r->captures_data;

            return cap[n + 1] - cap[n]

                   + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],

                                        NGX_ESCAPE_ARGS);

        } else {

            return cap[n + 1] - cap[n];

        }

    }

    return 0;

}

void

‌ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)

{

    int                                  *cap;

    u_char                               *p, *pos;

    ngx_uint_t                            n;

    ngx_http_request_t                   *r;

    ngx_http_script_copy_capture_code_t  *code;

    r = e->request;

    code = (ngx_http_script_copy_capture_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_copy_capture_code_t);

    n = code->n;

    pos = e->pos;

    if (n < r->ncaptures) {

        cap = r->captures;

        p = r->captures_data;

        if ((e->is_args || e->quote)

            && (e->request->quoted_uri || e->request->plus_in_uri))

        {

            e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],

                                               cap[n + 1] - cap[n],

                                               NGX_ESCAPE_ARGS);

        } else {

            e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script capture: \"%*s\"", e->pos - pos, pos);

}

#endif

static ngx_int_t

‌ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc)

{

    ngx_http_script_full_name_code_t  *code;

    code = ngx_http_script_add_code(*sc->lengths,

                                    sizeof(ngx_http_script_full_name_code_t),

                                    NULL);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code;

    code->conf_prefix = sc->conf_prefix;

    code = ngx_http_script_add_code(*sc->values,

                                    sizeof(ngx_http_script_full_name_code_t),

                                    &sc->main);

    if (code == NULL) {

        return NGX_ERROR;

    }

    code->code = ngx_http_script_full_name_code;

    code->conf_prefix = sc->conf_prefix;

    return NGX_OK;

}

static size_t

‌ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_full_name_code_t  *code;

    code = (ngx_http_script_full_name_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_full_name_code_t);

    return code->conf_prefix ? ngx_cycle->conf_prefix.len:

                               ngx_cycle->prefix.len;

}

static void

‌ngx_http_script_full_name_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_full_name_code_t  *code;

    ngx_str_t  value, *prefix;

    code = (ngx_http_script_full_name_code_t *) e->ip;

    value.data = e->buf.data;

    value.len = e->pos - e->buf.data;

    prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:

                                 (ngx_str_t *) &ngx_cycle->prefix;

    if (ngx_get_full_name(e->request->pool, prefix, &value) != NGX_OK) {

        e->ip = ngx_http_script_exit;

        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

        return;

    }

    e->buf = value;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script fullname: \"%V\"", &value);

    e->ip += sizeof(ngx_http_script_full_name_code_t);

}

void

‌ngx_http_script_return_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_return_code_t  *code;

    code = (ngx_http_script_return_code_t *) e->ip;

    if (code->status < NGX_HTTP_BAD_REQUEST

        || code->text.value.len

        || code->text.lengths)

    {

        e->status = ngx_http_send_response(e->request, code->status, NULL,

                                           &code->text);

    } else {

        e->status = code->status;

    }

    e->ip = ngx_http_script_exit;

}

void

‌ngx_http_script_break_code(ngx_http_script_engine_t *e)

{

    e->request->uri_changed = 0;

    e->ip = ngx_http_script_exit;

}

void

‌ngx_http_script_if_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_if_code_t  *code;

    code = (ngx_http_script_if_code_t *) e->ip;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script if");

    e->sp--;

    if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {

        if (code->loc_conf) {

            e->request->loc_conf = code->loc_conf;

            ngx_http_update_location_config(e->request);

        }

        e->ip += sizeof(ngx_http_script_if_code_t);

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script if: false");

    e->ip += code->next;

}

void

‌ngx_http_script_equal_code(ngx_http_script_engine_t *e)

{

    ngx_http_variable_value_t  *val, *res;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script equal");

    e->sp--;

    val = e->sp;

    res = e->sp - 1;

    e->ip += sizeof(uintptr_t);

    if (val->len == res->len

        && ngx_strncmp(val->data, res->data, res->len) == 0)

    {

        *res = ngx_http_variable_true_value;

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script equal: no");

    *res = ngx_http_variable_null_value;

}

void

‌ngx_http_script_not_equal_code(ngx_http_script_engine_t *e)

{

    ngx_http_variable_value_t  *val, *res;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script not equal");

    e->sp--;

    val = e->sp;

    res = e->sp - 1;

    e->ip += sizeof(uintptr_t);

    if (val->len == res->len

        && ngx_strncmp(val->data, res->data, res->len) == 0)

    {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                       "http script not equal: no");

        *res = ngx_http_variable_null_value;

        return;

    }

    *res = ngx_http_variable_true_value;

}

void

‌ngx_http_script_file_code(ngx_http_script_engine_t *e)

{

    ngx_str_t                     path;

    ngx_http_request_t           *r;

    ngx_open_file_info_t          of;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_variable_value_t    *value;

    ngx_http_script_file_code_t  *code;

    value = e->sp - 1;

    code = (ngx_http_script_file_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_file_code_t);

    path.len = value->len - 1;

    path.data = value->data;

    r = e->request;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http script file op %p \"%V\"", code->op, &path);

    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.test_only = 1;

    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) {

        e->ip = ngx_http_script_exit;

        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

        return;

    }

    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)

        != NGX_OK)

    {

        if (of.err != NGX_ENOENT

            && of.err != NGX_ENOTDIR

            && of.err != NGX_ENAMETOOLONG)

        {

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,

                          "%s \"%s\" failed", of.failed, value->data);

        }

        switch (code->op) {

        case ngx_http_script_file_plain:

        case ngx_http_script_file_dir:

        case ngx_http_script_file_exists:

        case ngx_http_script_file_exec:

             goto false_value;

        case ngx_http_script_file_not_plain:

        case ngx_http_script_file_not_dir:

        case ngx_http_script_file_not_exists:

        case ngx_http_script_file_not_exec:

             goto true_value;

        }

        goto false_value;

    }

    switch (code->op) {

    case ngx_http_script_file_plain:

        if (of.is_file) {

             goto true_value;

        }

        goto false_value;

    case ngx_http_script_file_not_plain:

        if (of.is_file) {

            goto false_value;

        }

        goto true_value;

    case ngx_http_script_file_dir:

        if (of.is_dir) {

             goto true_value;

        }

        goto false_value;

    case ngx_http_script_file_not_dir:

        if (of.is_dir) {

            goto false_value;

        }

        goto true_value;

    case ngx_http_script_file_exists:

        if (of.is_file || of.is_dir || of.is_link) {

             goto true_value;

        }

        goto false_value;

    case ngx_http_script_file_not_exists:

        if (of.is_file || of.is_dir || of.is_link) {

            goto false_value;

        }

        goto true_value;

    case ngx_http_script_file_exec:

        if (of.is_exec) {

             goto true_value;

        }

        goto false_value;

    case ngx_http_script_file_not_exec:

        if (of.is_exec) {

            goto false_value;

        }

        goto true_value;

    }

false_value:

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http script file op false");

    *value = ngx_http_variable_null_value;

    return;

true_value:

    *value = ngx_http_variable_true_value;

    return;

}

void

‌ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)

{

    size_t                                 len;

    ngx_http_script_engine_t               le;

    ngx_http_script_len_code_pt            lcode;

    ngx_http_script_complex_value_code_t  *code;

    code = (ngx_http_script_complex_value_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_complex_value_code_t);

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script complex value");

    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

    le.ip = code->lengths->elts;

    le.line = e->line;

    le.request = e->request;

    le.quote = e->quote;

    for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {

        lcode = *(ngx_http_script_len_code_pt *) le.ip;

    }

    e->buf.len = len;

    e->buf.data = ngx_pnalloc(e->request->pool, len);

    if (e->buf.data == NULL) {

        e->ip = ngx_http_script_exit;

        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;

        return;

    }

    e->pos = e->buf.data;

    e->sp->len = e->buf.len;

    e->sp->data = e->buf.data;

    e->sp++;

}

void

‌ngx_http_script_value_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_value_code_t  *code;

    code = (ngx_http_script_value_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_value_code_t);

    e->sp->len = code->text_len;

    e->sp->data = (u_char *) code->text_data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script value: \"%v\"", e->sp);

    e->sp++;

}

void

‌ngx_http_script_set_var_code(ngx_http_script_engine_t *e)

{

    ngx_http_request_t          *r;

    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    r = e->request;

    e->sp--;

    r->variables[code->index].len = e->sp->len;

    r->variables[code->index].valid = 1;

    r->variables[code->index].no_cacheable = 0;

    r->variables[code->index].not_found = 0;

    r->variables[code->index].data = e->sp->data;

#if (NGX_DEBUG)

    {

    ngx_http_variable_t        *v;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    v = cmcf->variables.elts;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script set $%V", &v[code->index].name);

    }

#endif

}

void

‌ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e)

{

    ngx_http_script_var_handler_code_t  *code;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script set var handler");

    code = (ngx_http_script_var_handler_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_handler_code_t);

    e->sp--;

    code->handler(e->request, e->sp, code->data);

}

void

‌ngx_http_script_var_code(ngx_http_script_engine_t *e)

{

    ngx_http_variable_value_t   *value;

    ngx_http_script_var_code_t  *code;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                   "http script var");

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    value = ngx_http_get_flushed_variable(e->request, code->index);

    if (value && !value->not_found) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,

                       "http script var: \"%v\"", value);

        *e->sp = *value;

        e->sp++;

        return;

    }

    *e->sp = ngx_http_variable_null_value;

    e->sp++;

}

void

‌ngx_http_script_nop_code(ngx_http_script_engine_t *e)

{

    e->ip += sizeof(uintptr_t);

}

src/http/ngx_http_script.h - nginx-1.7.9

Data types defined

ngx_http_compile_complex_value_t

ngx_http_complex_value_t

ngx_http_script_code_pt

ngx_http_script_compile_t

ngx_http_script_complex_value_code_t

ngx_http_script_copy_capture_code_t

ngx_http_script_copy_code_t

ngx_http_script_engine_t

ngx_http_script_file_code_t

ngx_http_script_file_op_e

ngx_http_script_full_name_code_t

ngx_http_script_if_code_t

ngx_http_script_len_code_pt

ngx_http_script_regex_code_t

ngx_http_script_regex_end_code_t

ngx_http_script_return_code_t

ngx_http_script_value_code_t

ngx_http_script_var_code_t

ngx_http_script_var_handler_code_t

Macros defined

_NGX_HTTP_SCRIPT_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_

‌#define _NGX_HTTP_SCRIPT_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    u_char                     *ip;

    u_char                     *pos;

    ngx_http_variable_value_t  *sp;

    ngx_str_t                   buf;

    ngx_str_t                   line;

    /* the start of the rewritten arguments */

    u_char                     *args;

    unsigned                    flushed:1;

    unsigned                    skip:1;

    unsigned                    quote:1;

    unsigned                    is_args:1;

    unsigned                    log:1;

    ngx_int_t                   status;

    ngx_http_request_t         *request;

‌} ngx_http_script_engine_t;

typedef struct {

    ngx_conf_t                 *cf;

    ngx_str_t                  *source;

    ngx_array_t               **flushes;

    ngx_array_t               **lengths;

    ngx_array_t               **values;

    ngx_uint_t                  variables;

    ngx_uint_t                  ncaptures;

    ngx_uint_t                  captures_mask;

    ngx_uint_t                  size;

    void                       *main;

    unsigned                    compile_args:1;

    unsigned                    complete_lengths:1;

    unsigned                    complete_values:1;

    unsigned                    zero:1;

    unsigned                    conf_prefix:1;

    unsigned                    root_prefix:1;

    unsigned                    dup_capture:1;

    unsigned                    args:1;

‌} ngx_http_script_compile_t;

typedef struct {

    ngx_str_t                   value;

    ngx_uint_t                 *flushes;

    void                       *lengths;

    void                       *values;

‌} ngx_http_complex_value_t;

typedef struct {

    ngx_conf_t                 *cf;

    ngx_str_t                  *value;

    ngx_http_complex_value_t   *complex_value;

    unsigned                    zero:1;

    unsigned                    conf_prefix:1;

    unsigned                    root_prefix:1;

‌} ngx_http_compile_complex_value_t;

‌typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);

‌typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   len;

‌} ngx_http_script_copy_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   index;

‌} ngx_http_script_var_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    ngx_http_set_variable_pt    handler;

    uintptr_t                   data;

‌} ngx_http_script_var_handler_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   n;

‌} ngx_http_script_copy_capture_code_t;

#if (NGX_PCRE)

typedef struct {

    ngx_http_script_code_pt     code;

    ngx_http_regex_t           *regex;

    ngx_array_t                *lengths;

    uintptr_t                   size;

    uintptr_t                   status;

    uintptr_t                   next;

    uintptr_t                   test:1;

    uintptr_t                   negative_test:1;

    uintptr_t                   uri:1;

    uintptr_t                   args:1;

    /* add the r->args to the new arguments */

    uintptr_t                   add_args:1;

    uintptr_t                   redirect:1;

    uintptr_t                   break_cycle:1;

    ngx_str_t                   name;

‌} ngx_http_script_regex_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   uri:1;

    uintptr_t                   args:1;

    /* add the r->args to the new arguments */

    uintptr_t                   add_args:1;

    uintptr_t                   redirect:1;

‌} ngx_http_script_regex_end_code_t;

#endif

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   conf_prefix;

‌} ngx_http_script_full_name_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   status;

    ngx_http_complex_value_t    text;

‌} ngx_http_script_return_code_t;

typedef enum {

    ngx_http_script_file_plain = 0,

    ngx_http_script_file_not_plain,

    ngx_http_script_file_dir,

    ngx_http_script_file_not_dir,

    ngx_http_script_file_exists,

    ngx_http_script_file_not_exists,

    ngx_http_script_file_exec,

    ngx_http_script_file_not_exec

‌} ngx_http_script_file_op_e;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   op;

‌} ngx_http_script_file_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   next;

    void                      **loc_conf;

‌} ngx_http_script_if_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    ngx_array_t                *lengths;

‌} ngx_http_script_complex_value_code_t;

typedef struct {

    ngx_http_script_code_pt     code;

    uintptr_t                   value;

    uintptr_t                   text_len;

    uintptr_t                   text_data;

‌} ngx_http_script_value_code_t;

void ngx_http_script_flush_complex_value(ngx_http_request_t *r,

    ngx_http_complex_value_t *val);

ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,

    ngx_http_complex_value_t *val, ngx_str_t *value);

ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);

char *ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,

    ngx_array_t *predicates);

char *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);

ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);

u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,

    void *code_lengths, size_t reserved, void *code_values);

void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,

    ngx_array_t *indices);

void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,

    size_t size);

void *ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code);

size_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e);

void ngx_http_script_copy_code(ngx_http_script_engine_t *e);

size_t ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e);

void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);

size_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);

void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);

size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);

void ngx_http_script_start_args_code(ngx_http_script_engine_t *e);

#if (NGX_PCRE)

void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);

void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);

#endif

void ngx_http_script_return_code(ngx_http_script_engine_t *e);

void ngx_http_script_break_code(ngx_http_script_engine_t *e);

void ngx_http_script_if_code(ngx_http_script_engine_t *e);

void ngx_http_script_equal_code(ngx_http_script_engine_t *e);

void ngx_http_script_not_equal_code(ngx_http_script_engine_t *e);

void ngx_http_script_file_code(ngx_http_script_engine_t *e);

void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);

void ngx_http_script_value_code(ngx_http_script_engine_t *e);

void ngx_http_script_set_var_code(ngx_http_script_engine_t *e);

void ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e);

void ngx_http_script_var_code(ngx_http_script_engine_t *e);

void ngx_http_script_nop_code(ngx_http_script_engine_t *e);

#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */

src/core/ngx_open_file_cache.h - nginx-1.7.9

Data types defined

ngx_cached_open_file_s

ngx_cached_open_file_t

ngx_open_file_cache_cleanup_t

ngx_open_file_cache_event_t

ngx_open_file_cache_t

ngx_open_file_info_t

Macros defined

NGX_OPEN_FILE_DIRECTIO_OFF

_NGX_OPEN_FILE_CACHE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_

‌#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_

‌#define NGX_OPEN_FILE_DIRECTIO_OFF  NGX_MAX_OFF_T_VALUE

typedef struct {

    ngx_fd_t                 fd;

    ngx_file_uniq_t          uniq;

    time_t                   mtime;

    off_t                    size;

    off_t                    fs_size;

    off_t                    directio;

    size_t                   read_ahead;

    ngx_err_t                err;

    char                    *failed;

    time_t                   valid;

    ngx_uint_t               min_uses;

#if (NGX_HAVE_OPENAT)

    size_t                   disable_symlinks_from;

    unsigned                 disable_symlinks:2;

#endif

    unsigned                 test_dir:1;

    unsigned                 test_only:1;

    unsigned                 log:1;

    unsigned                 errors:1;

    unsigned                 events:1;

    unsigned                 is_dir:1;

    unsigned                 is_file:1;

    unsigned                 is_link:1;

    unsigned                 is_exec:1;

    unsigned                 is_directio:1;

‌} ngx_open_file_info_t;

‌typedef struct ngx_cached_open_file_s  ngx_cached_open_file_t;

‌struct ngx_cached_open_file_s {

    ngx_rbtree_node_t        node;

    ngx_queue_t              queue;

    u_char                  *name;

    time_t                   created;

    time_t                   accessed;

    ngx_fd_t                 fd;

    ngx_file_uniq_t          uniq;

    time_t                   mtime;

    off_t                    size;

    ngx_err_t                err;

    uint32_t                 uses;

#if (NGX_HAVE_OPENAT)

    size_t                   disable_symlinks_from;

    unsigned                 disable_symlinks:2;

#endif

    unsigned                 count:24;

    unsigned                 close:1;

    unsigned                 use_event:1;

    unsigned                 is_dir:1;

    unsigned                 is_file:1;

    unsigned                 is_link:1;

    unsigned                 is_exec:1;

    unsigned                 is_directio:1;

    ngx_event_t             *event;

};

typedef struct {

    ngx_rbtree_t             rbtree;

    ngx_rbtree_node_t        sentinel;

    ngx_queue_t              expire_queue;

    ngx_uint_t               current;

    ngx_uint_t               max;

    time_t                   inactive;

‌} ngx_open_file_cache_t;

typedef struct {

    ngx_open_file_cache_t   *cache;

    ngx_cached_open_file_t  *file;

    ngx_uint_t               min_uses;

    ngx_log_t               *log;

‌} ngx_open_file_cache_cleanup_t;

typedef struct {

    /* ngx_connection_t stub to allow use c->fd as event ident */

    void                    *data;

    ngx_event_t             *read;

    ngx_event_t             *write;

    ngx_fd_t                 fd;

    ngx_cached_open_file_t  *file;

    ngx_open_file_cache_t   *cache;

‌} ngx_open_file_cache_event_t;

ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool,

    ngx_uint_t max, time_t inactive);

ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,

    ngx_open_file_info_t *of, ngx_pool_t *pool);

#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */

src/core/ngx_open_file_cache.c - nginx-1.7.9

Functions defined

ngx_close_cached_file

ngx_expire_old_cached_files

ngx_file_info_wrapper

ngx_file_o_path_info

ngx_open_and_stat_file

ngx_open_cached_file

ngx_open_file_add_event

ngx_open_file_cache_cleanup

ngx_open_file_cache_init

ngx_open_file_cache_rbtree_insert_value

ngx_open_file_cache_remove

ngx_open_file_cleanup

ngx_open_file_del_event

ngx_open_file_lookup

ngx_open_file_wrapper

ngx_openat_file_owner

Macros defined

NGX_MIN_READ_AHEAD

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

/*

* open file cache caches

*    open file handles with stat() info;

*    directories stat() info;

*    files and directories errors: not found, access denied, etc.

*/

‌#define NGX_MIN_READ_AHEAD  (128 * 1024)

static void ngx_open_file_cache_cleanup(void *data);

#if (NGX_HAVE_OPENAT)

static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,

    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);

#if (NGX_HAVE_O_PATH)

static ngx_int_t ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi,

    ngx_log_t *log);

#endif

#endif

static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,

    ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,

    ngx_int_t access, ngx_log_t *log);

static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,

    ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);

static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,

    ngx_open_file_info_t *of, ngx_log_t *log);

static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,

    ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);

static void ngx_open_file_cleanup(void *data);

static void ngx_close_cached_file(ngx_open_file_cache_t *cache,

    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);

static void ngx_open_file_del_event(ngx_cached_open_file_t *file);

static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,

    ngx_uint_t n, ngx_log_t *log);

static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

static ngx_cached_open_file_t *

    ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,

    uint32_t hash);

static void ngx_open_file_cache_remove(ngx_event_t *ev);

ngx_open_file_cache_t *

‌ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)

{

    ngx_pool_cleanup_t     *cln;

    ngx_open_file_cache_t  *cache;

    cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));

    if (cache == NULL) {

        return NULL;

    }

    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,

                    ngx_open_file_cache_rbtree_insert_value);

    ngx_queue_init(&cache->expire_queue);

    cache->current = 0;

    cache->max = max;

    cache->inactive = inactive;

    cln = ngx_pool_cleanup_add(pool, 0);

    if (cln == NULL) {

        return NULL;

    }

    cln->handler = ngx_open_file_cache_cleanup;

    cln->data = cache;

    return cache;

}

static void

‌ngx_open_file_cache_cleanup(void *data)

{

    ngx_open_file_cache_t  *cache = data;

    ngx_queue_t             *q;

    ngx_cached_open_file_t  *file;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                   "open file cache cleanup");

    for ( ;; ) {

        if (ngx_queue_empty(&cache->expire_queue)) {

            break;

        }

        q = ngx_queue_last(&cache->expire_queue);

        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);

        ngx_queue_remove(q);

        ngx_rbtree_delete(&cache->rbtree, &file->node);

        cache->current--;

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                       "delete cached open file: %s", file->name);

        if (!file->err && !file->is_dir) {

            file->close = 1;

            file->count = 0;

            ngx_close_cached_file(cache, file, 0, ngx_cycle->log);

        } else {

            ngx_free(file->name);

            ngx_free(file);

        }

    }

    if (cache->current) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                      "%ui items still leave in open file cache",

                      cache->current);

    }

    if (cache->rbtree.root != cache->rbtree.sentinel) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                      "rbtree still is not empty in open file cache");

    }

}

ngx_int_t

‌ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,

    ngx_open_file_info_t *of, ngx_pool_t *pool)

{

    time_t                          now;

    uint32_t                        hash;

    ngx_int_t                       rc;

    ngx_file_info_t                 fi;

    ngx_pool_cleanup_t             *cln;

    ngx_cached_open_file_t         *file;

    ngx_pool_cleanup_file_t        *clnf;

    ngx_open_file_cache_cleanup_t  *ofcln;

    of->fd = NGX_INVALID_FILE;

    of->err = 0;

    if (cache == NULL) {

        if (of->test_only) {

            if (ngx_file_info_wrapper(name, of, &fi, pool->log)

                == NGX_FILE_ERROR)

            {

                return NGX_ERROR;

            }

            of->uniq = ngx_file_uniq(&fi);

            of->mtime = ngx_file_mtime(&fi);

            of->size = ngx_file_size(&fi);

            of->fs_size = ngx_file_fs_size(&fi);

            of->is_dir = ngx_is_dir(&fi);

            of->is_file = ngx_is_file(&fi);

            of->is_link = ngx_is_link(&fi);

            of->is_exec = ngx_is_exec(&fi);

            return NGX_OK;

        }

        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));

        if (cln == NULL) {

            return NGX_ERROR;

        }

        rc = ngx_open_and_stat_file(name, of, pool->log);

        if (rc == NGX_OK && !of->is_dir) {

            cln->handler = ngx_pool_cleanup_file;

            clnf = cln->data;

            clnf->fd = of->fd;

            clnf->name = name->data;

            clnf->log = pool->log;

        }

        return rc;

    }

    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));

    if (cln == NULL) {

        return NGX_ERROR;

    }

    now = ngx_time();

    hash = ngx_crc32_long(name->data, name->len);

    file = ngx_open_file_lookup(cache, name, hash);

    if (file) {

        file->uses++;

        ngx_queue_remove(&file->queue);

        if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {

            /* file was not used often enough to keep open */

            rc = ngx_open_and_stat_file(name, of, pool->log);

            if (rc != NGX_OK && (of->err == 0 || !of->errors)) {

                goto failed;

            }

            goto add_event;

        }

        if (file->use_event

            || (file->event == NULL

                && (of->uniq == 0 || of->uniq == file->uniq)

                && now - file->created < of->valid

#if (NGX_HAVE_OPENAT)

                && of->disable_symlinks == file->disable_symlinks

                && of->disable_symlinks_from == file->disable_symlinks_from

#endif

            ))

        {

            if (file->err == 0) {

                of->fd = file->fd;

                of->uniq = file->uniq;

                of->mtime = file->mtime;

                of->size = file->size;

                of->is_dir = file->is_dir;

                of->is_file = file->is_file;

                of->is_link = file->is_link;

                of->is_exec = file->is_exec;

                of->is_directio = file->is_directio;

                if (!file->is_dir) {

                    file->count++;

                    ngx_open_file_add_event(cache, file, of, pool->log);

                }

            } else {

                of->err = file->err;

#if (NGX_HAVE_OPENAT)

                of->failed = file->disable_symlinks ? ngx_openat_file_n

                                                    : ngx_open_file_n;

#else

                of->failed = ngx_open_file_n;

#endif

            }

            goto found;

        }

        ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,

                       "retest open file: %s, fd:%d, c:%d, e:%d",

                       file->name, file->fd, file->count, file->err);

        if (file->is_dir) {

            /*

             * chances that directory became file are very small

             * so test_dir flag allows to use a single syscall

             * in ngx_file_info() instead of three syscalls

             */

            of->test_dir = 1;

        }

        of->fd = file->fd;

        of->uniq = file->uniq;

        rc = ngx_open_and_stat_file(name, of, pool->log);

        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {

            goto failed;

        }

        if (of->is_dir) {

            if (file->is_dir || file->err) {

                goto update;

            }

            /* file became directory */

        } else if (of->err == 0) {  /* file */

            if (file->is_dir || file->err) {

                goto add_event;

            }

            if (of->uniq == file->uniq) {

                if (file->event) {

                    file->use_event = 1;

                }

                of->is_directio = file->is_directio;

                goto update;

            }

            /* file was changed */

        } else { /* error to cache */

            if (file->err || file->is_dir) {

                goto update;

            }

            /* file was removed, etc. */

        }

        if (file->count == 0) {

            ngx_open_file_del_event(file);

            if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,

                              ngx_close_file_n " \"%V\" failed", name);

            }

            goto add_event;

        }

        ngx_rbtree_delete(&cache->rbtree, &file->node);

        cache->current--;

        file->close = 1;

        goto create;

    }

    /* not found */

    rc = ngx_open_and_stat_file(name, of, pool->log);

    if (rc != NGX_OK && (of->err == 0 || !of->errors)) {

        goto failed;

    }

create:

    if (cache->current >= cache->max) {

        ngx_expire_old_cached_files(cache, 0, pool->log);

    }

    file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);

    if (file == NULL) {

        goto failed;

    }

    file->name = ngx_alloc(name->len + 1, pool->log);

    if (file->name == NULL) {

        ngx_free(file);

        file = NULL;

        goto failed;

    }

    ngx_cpystrn(file->name, name->data, name->len + 1);

    file->node.key = hash;

    ngx_rbtree_insert(&cache->rbtree, &file->node);

    cache->current++;

    file->uses = 1;

    file->count = 0;

    file->use_event = 0;

    file->event = NULL;

add_event:

    ngx_open_file_add_event(cache, file, of, pool->log);

update:

    file->fd = of->fd;

    file->err = of->err;

#if (NGX_HAVE_OPENAT)

    file->disable_symlinks = of->disable_symlinks;

    file->disable_symlinks_from = of->disable_symlinks_from;

#endif

    if (of->err == 0) {

        file->uniq = of->uniq;

        file->mtime = of->mtime;

        file->size = of->size;

        file->close = 0;

        file->is_dir = of->is_dir;

        file->is_file = of->is_file;

        file->is_link = of->is_link;

        file->is_exec = of->is_exec;

        file->is_directio = of->is_directio;

        if (!of->is_dir) {

            file->count++;

        }

    }

    file->created = now;

found:

    file->accessed = now;

    ngx_queue_insert_head(&cache->expire_queue, &file->queue);

    ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,

                   "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",

                   file->name, file->fd, file->count, file->err, file->uses);

    if (of->err == 0) {

        if (!of->is_dir) {

            cln->handler = ngx_open_file_cleanup;

            ofcln = cln->data;

            ofcln->cache = cache;

            ofcln->file = file;

            ofcln->min_uses = of->min_uses;

            ofcln->log = pool->log;

        }

        return NGX_OK;

    }

    return NGX_ERROR;

failed:

    if (file) {

        ngx_rbtree_delete(&cache->rbtree, &file->node);

        cache->current--;

        if (file->count == 0) {

            if (file->fd != NGX_INVALID_FILE) {

                if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {

                    ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,

                                  ngx_close_file_n " \"%s\" failed",

                                  file->name);

                }

            }

            ngx_free(file->name);

            ngx_free(file);

        } else {

            file->close = 1;

        }

    }

    if (of->fd != NGX_INVALID_FILE) {

        if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,

                          ngx_close_file_n " \"%V\" failed", name);

        }

    }

    return NGX_ERROR;

}

#if (NGX_HAVE_OPENAT)

static ngx_fd_t

‌ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,

    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)

{

    ngx_fd_t         fd;

    ngx_err_t        err;

    ngx_file_info_t  fi, atfi;

    /*

     * To allow symlinks with the same owner, use openat() (followed

     * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare

     * uids between fstat() and fstatat().

     *

     * As there is a race between openat() and fstatat() we don't

     * know if openat() in fact opened symlink or not.  Therefore,

     * we have to compare uids even if fstatat() reports the opened

     * component isn't a symlink (as we don't know whether it was

     * symlink during openat() or not).

     */

    fd = ngx_openat_file(at_fd, name, mode, create, access);

    if (fd == NGX_INVALID_FILE) {

        return NGX_INVALID_FILE;

    }

    if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)

        == NGX_FILE_ERROR)

    {

        err = ngx_errno;

        goto failed;

    }

#if (NGX_HAVE_O_PATH)

    if (ngx_file_o_path_info(fd, &fi, log) == NGX_ERROR) {

        err = ngx_errno;

        goto failed;

    }

#else

    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {

        err = ngx_errno;

        goto failed;

    }

#endif

    if (fi.st_uid != atfi.st_uid) {

        err = NGX_ELOOP;

        goto failed;

    }

    return fd;

failed:

    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_close_file_n " \"%V\" failed", name);

    }

    ngx_set_errno(err);

    return NGX_INVALID_FILE;

}

#if (NGX_HAVE_O_PATH)

static ngx_int_t

‌ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, ngx_log_t *log)

{

    static ngx_uint_t  use_fstat = 1;

    /*

     * In Linux 2.6.39 the O_PATH flag was introduced that allows to obtain

     * a descriptor without actually opening file or directory.  It requires

     * less permissions for path components, but till Linux 3.6 fstat() returns

     * EBADF on such descriptors, and fstatat() with the AT_EMPTY_PATH flag

     * should be used instead.

     *

     * Three scenarios are handled in this function:

     *

     * 1) The kernel is newer than 3.6 or fstat() with O_PATH support was

     *    backported by vendor.  Then fstat() is used.

     *

     * 2) The kernel is newer than 2.6.39 but older than 3.6.  In this case

     *    the first call of fstat() returns EBADF and we fallback to fstatat()

     *    with AT_EMPTY_PATH which was introduced at the same time as O_PATH.

     *

     * 3) The kernel is older than 2.6.39 but nginx was build with O_PATH

     *    support.  Since descriptors are opened with O_PATH|O_RDONLY flags

     *    and O_PATH is ignored by the kernel then the O_RDONLY flag is

     *    actually used.  In this case fstat() just works.

     */

    if (use_fstat) {

        if (ngx_fd_info(fd, fi) != NGX_FILE_ERROR) {

            return NGX_OK;

        }

        if (ngx_errno != NGX_EBADF) {

            return NGX_ERROR;

        }

        ngx_log_error(NGX_LOG_NOTICE, log, 0,

                      "fstat(O_PATH) failed with EBADF, "

                      "switching to fstatat(AT_EMPTY_PATH)");

        use_fstat = 0;

    }

    if (ngx_file_at_info(fd, "", fi, AT_EMPTY_PATH) != NGX_FILE_ERROR) {

        return NGX_OK;

    }

    return NGX_ERROR;

}

#endif

#endif /* NGX_HAVE_OPENAT */

static ngx_fd_t

‌ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,

    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)

{

    ngx_fd_t  fd;

#if !(NGX_HAVE_OPENAT)

    fd = ngx_open_file(name->data, mode, create, access);

    if (fd == NGX_INVALID_FILE) {

        of->err = ngx_errno;

        of->failed = ngx_open_file_n;

        return NGX_INVALID_FILE;

    }

    return fd;

#else

    u_char           *p, *cp, *end;

    ngx_fd_t          at_fd;

    ngx_str_t         at_name;

    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {

        fd = ngx_open_file(name->data, mode, create, access);

        if (fd == NGX_INVALID_FILE) {

            of->err = ngx_errno;

            of->failed = ngx_open_file_n;

            return NGX_INVALID_FILE;

        }

        return fd;

    }

    p = name->data;

    end = p + name->len;

    at_name = *name;

    if (of->disable_symlinks_from) {

        cp = p + of->disable_symlinks_from;

        *cp = '\0';

        at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,

                              NGX_FILE_OPEN, 0);

        *cp = '/';

        if (at_fd == NGX_INVALID_FILE) {

            of->err = ngx_errno;

            of->failed = ngx_open_file_n;

            return NGX_INVALID_FILE;

        }

        at_name.len = of->disable_symlinks_from;

        p = cp + 1;

    } else if (*p == '/') {

        at_fd = ngx_open_file("/",

                              NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,

                              NGX_FILE_OPEN, 0);

        if (at_fd == NGX_INVALID_FILE) {

            of->err = ngx_errno;

            of->failed = ngx_openat_file_n;

            return NGX_INVALID_FILE;

        }

        at_name.len = 1;

        p++;

    } else {

        at_fd = NGX_AT_FDCWD;

    }

    for ( ;; ) {

        cp = ngx_strlchr(p, end, '/');

        if (cp == NULL) {

            break;

        }

        if (cp == p) {

            p++;

            continue;

        }

        *cp = '\0';

        if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {

            fd = ngx_openat_file_owner(at_fd, p,

                                       NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,

                                       NGX_FILE_OPEN, 0, log);

        } else {

            fd = ngx_openat_file(at_fd, p,

                           NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,

                           NGX_FILE_OPEN, 0);

        }

        *cp = '/';

        if (fd == NGX_INVALID_FILE) {

            of->err = ngx_errno;

            of->failed = ngx_openat_file_n;

            goto failed;

        }

        if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%V\" failed", &at_name);

        }

        p = cp + 1;

        at_fd = fd;

        at_name.len = cp - at_name.data;

    }

    if (p == end) {

        /*

         * If pathname ends with a trailing slash, assume the last path

         * component is a directory and reopen it with requested flags;

         * if not, fail with ENOTDIR as per POSIX.

         *

         * We cannot rely on O_DIRECTORY in the loop above to check

         * that the last path component is a directory because

         * O_DIRECTORY doesn't work on FreeBSD 8.  Fortunately, by

         * reopening a directory, we don't depend on it at all.

         */

        fd = ngx_openat_file(at_fd, ".", mode, create, access);

        goto done;

    }

    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER

        && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))

    {

        fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);

    } else {

        fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);

    }

done:

    if (fd == NGX_INVALID_FILE) {

        of->err = ngx_errno;

        of->failed = ngx_openat_file_n;

    }

failed:

    if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_close_file_n " \"%V\" failed", &at_name);

    }

    return fd;

#endif

}

static ngx_int_t

‌ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,

    ngx_file_info_t *fi, ngx_log_t *log)

{

    ngx_int_t  rc;

#if !(NGX_HAVE_OPENAT)

    rc = ngx_file_info(name->data, fi);

    if (rc == NGX_FILE_ERROR) {

        of->err = ngx_errno;

        of->failed = ngx_file_info_n;

        return NGX_FILE_ERROR;

    }

    return rc;

#else

    ngx_fd_t  fd;

    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {

        rc = ngx_file_info(name->data, fi);

        if (rc == NGX_FILE_ERROR) {

            of->err = ngx_errno;

            of->failed = ngx_file_info_n;

            return NGX_FILE_ERROR;

        }

        return rc;

    }

    fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,

                               NGX_FILE_OPEN, 0, log);

    if (fd == NGX_INVALID_FILE) {

        return NGX_FILE_ERROR;

    }

    rc = ngx_fd_info(fd, fi);

    if (rc == NGX_FILE_ERROR) {

        of->err = ngx_errno;

        of->failed = ngx_fd_info_n;

    }

    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_close_file_n " \"%V\" failed", name);

    }

    return rc;

#endif

}

static ngx_int_t

‌ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,

    ngx_log_t *log)

{

    ngx_fd_t         fd;

    ngx_file_info_t  fi;

    if (of->fd != NGX_INVALID_FILE) {

        if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {

            of->fd = NGX_INVALID_FILE;

            return NGX_ERROR;

        }

        if (of->uniq == ngx_file_uniq(&fi)) {

            goto done;

        }

    } else if (of->test_dir) {

        if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {

            of->fd = NGX_INVALID_FILE;

            return NGX_ERROR;

        }

        if (ngx_is_dir(&fi)) {

            goto done;

        }

    }

    if (!of->log) {

        /*

         * Use non-blocking open() not to hang on FIFO files, etc.

         * This flag has no effect on a regular files.

         */

        fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,

                                   NGX_FILE_OPEN, 0, log);

    } else {

        fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,

                                   NGX_FILE_CREATE_OR_OPEN,

                                   NGX_FILE_DEFAULT_ACCESS, log);

    }

    if (fd == NGX_INVALID_FILE) {

        of->fd = NGX_INVALID_FILE;

        return NGX_ERROR;

    }

    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,

                      ngx_fd_info_n " \"%V\" failed", name);

        if (ngx_close_file(fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%V\" failed", name);

        }

        of->fd = NGX_INVALID_FILE;

        return NGX_ERROR;

    }

    if (ngx_is_dir(&fi)) {

        if (ngx_close_file(fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%V\" failed", name);

        }

        of->fd = NGX_INVALID_FILE;

    } else {

        of->fd = fd;

        if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {

            if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {

                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                              ngx_read_ahead_n " \"%V\" failed", name);

            }

        }

        if (of->directio <= ngx_file_size(&fi)) {

            if (ngx_directio_on(fd) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                              ngx_directio_on_n " \"%V\" failed", name);

            } else {

                of->is_directio = 1;

            }

        }

    }

done:

    of->uniq = ngx_file_uniq(&fi);

    of->mtime = ngx_file_mtime(&fi);

    of->size = ngx_file_size(&fi);

    of->fs_size = ngx_file_fs_size(&fi);

    of->is_dir = ngx_is_dir(&fi);

    of->is_file = ngx_is_file(&fi);

    of->is_link = ngx_is_link(&fi);

    of->is_exec = ngx_is_exec(&fi);

    return NGX_OK;

}

/*

* we ignore any possible event setting error and

* fallback to usual periodic file retests

*/

static void

‌ngx_open_file_add_event(ngx_open_file_cache_t *cache,

    ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)

{

    ngx_open_file_cache_event_t  *fev;

    if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)

        || !of->events

        || file->event

        || of->fd == NGX_INVALID_FILE

        || file->uses < of->min_uses)

    {

        return;

    }

    file->use_event = 0;

    file->event = ngx_calloc(sizeof(ngx_event_t), log);

    if (file->event== NULL) {

        return;

    }

    fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);

    if (fev == NULL) {

        ngx_free(file->event);

        file->event = NULL;

        return;

    }

    fev->fd = of->fd;

    fev->file = file;

    fev->cache = cache;

    file->event->handler = ngx_open_file_cache_remove;

    file->event->data = fev;

    /*

     * although vnode event may be called while ngx_cycle->poll

     * destruction, however, cleanup procedures are run before any

     * memory freeing and events will be canceled.

     */

    file->event->log = ngx_cycle->log;

    if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)

        != NGX_OK)

    {

        ngx_free(file->event->data);

        ngx_free(file->event);

        file->event = NULL;

        return;

    }

    /*

     * we do not set file->use_event here because there may be a race

     * condition: a file may be deleted between opening the file and

     * adding event, so we rely upon event notification only after

     * one file revalidation on next file access

     */

    return;

}

static void

‌ngx_open_file_cleanup(void *data)

{

    ngx_open_file_cache_cleanup_t  *c = data;

    c->file->count--;

    ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);

    /* drop one or two expired open files */

    ngx_expire_old_cached_files(c->cache, 1, c->log);

}

static void

‌ngx_close_cached_file(ngx_open_file_cache_t *cache,

    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)

{

    ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,

                   "close cached open file: %s, fd:%d, c:%d, u:%d, %d",

                   file->name, file->fd, file->count, file->uses, file->close);

    if (!file->close) {

        file->accessed = ngx_time();

        ngx_queue_remove(&file->queue);

        ngx_queue_insert_head(&cache->expire_queue, &file->queue);

        if (file->uses >= min_uses || file->count) {

            return;

        }

    }

    ngx_open_file_del_event(file);

    if (file->count) {

        return;

    }

    if (file->fd != NGX_INVALID_FILE) {

        if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed", file->name);

        }

        file->fd = NGX_INVALID_FILE;

    }

    if (!file->close) {

        return;

    }

    ngx_free(file->name);

    ngx_free(file);

}

static void

‌ngx_open_file_del_event(ngx_cached_open_file_t *file)

{

    if (file->event == NULL) {

        return;

    }

    (void) ngx_del_event(file->event, NGX_VNODE_EVENT,

                         file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);

    ngx_free(file->event->data);

    ngx_free(file->event);

    file->event = NULL;

    file->use_event = 0;

}

static void

‌ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,

    ngx_log_t *log)

{

    time_t                   now;

    ngx_queue_t             *q;

    ngx_cached_open_file_t  *file;

    now = ngx_time();

    /*

     * n == 1 deletes one or two inactive files

     * n == 0 deletes least recently used file by force

     *        and one or two inactive files

     */

    while (n < 3) {

        if (ngx_queue_empty(&cache->expire_queue)) {

            return;

        }

        q = ngx_queue_last(&cache->expire_queue);

        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);

        if (n++ != 0 && now - file->accessed <= cache->inactive) {

            return;

        }

        ngx_queue_remove(q);

        ngx_rbtree_delete(&cache->rbtree, &file->node);

        cache->current--;

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,

                       "expire cached open file: %s", file->name);

        if (!file->err && !file->is_dir) {

            file->close = 1;

            ngx_close_cached_file(cache, file, 0, log);

        } else {

            ngx_free(file->name);

            ngx_free(file);

        }

    }

}

static void

‌ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

{

    ngx_rbtree_node_t       **p;

    ngx_cached_open_file_t    *file, *file_temp;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            file = (ngx_cached_open_file_t *) node;

            file_temp = (ngx_cached_open_file_t *) temp;

            p = (ngx_strcmp(file->name, file_temp->name) < 0)

                    ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

static ngx_cached_open_file_t *

‌ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,

    uint32_t hash)

{

    ngx_int_t                rc;

    ngx_rbtree_node_t       *node, *sentinel;

    ngx_cached_open_file_t  *file;

    node = cache->rbtree.root;

    sentinel = cache->rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        file = (ngx_cached_open_file_t *) node;

        rc = ngx_strcmp(name->data, file->name);

        if (rc == 0) {

            return file;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    return NULL;

}

static void

‌ngx_open_file_cache_remove(ngx_event_t *ev)

{

    ngx_cached_open_file_t       *file;

    ngx_open_file_cache_event_t  *fev;

    fev = ev->data;

    file = fev->file;

    ngx_queue_remove(&file->queue);

    ngx_rbtree_delete(&fev->cache->rbtree, &file->node);

    fev->cache->current--;

    /* NGX_ONESHOT_EVENT was already deleted */

    file->event = NULL;

    file->use_event = 0;

    file->close = 1;

    ngx_close_cached_file(fev->cache, file, 0, ev->log);

    /* free memory only when fev->cache and fev->file are already not needed */

    ngx_free(ev->data);

    ngx_free(ev);

}

src/http/ngx_http_parse_time.c - nginx-1.7.9

Global variables defined

mday

Functions defined

ngx_http_parse_time

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌static ngx_uint_t  mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

time_t

‌ngx_http_parse_time(u_char *value, size_t len)

{

    u_char      *p, *end;

    ngx_int_t    month;

    ngx_uint_t   day, year, hour, min, sec;

    uint64_t     time;

    enum {

        no = 0,

        rfc822,   /* Tue, 10 Nov 2002 23:50:13   */

        rfc850,   /* Tuesday, 10-Dec-02 23:50:13 */

        isoc      /* Tue Dec 10 23:50:13 2002    */

    } fmt;

    fmt = 0;

    end = value + len;

#if (NGX_SUPPRESS_WARN)

    day = 32;

    year = 2038;

#endif

    for (p = value; p < end; p++) {

        if (*p == ',') {

            break;

        }

        if (*p == ' ') {

            fmt = isoc;

            break;

        }

    }

    for (p++; p < end; p++)

        if (*p != ' ') {

            break;

        }

    if (end - p < 18) {

        return NGX_ERROR;

        }

    if (fmt != isoc) {

        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {

            return NGX_ERROR;

        }

        day = (*p - '0') * 10 + *(p + 1) - '0';

        p += 2;

        if (*p == ' ') {

            if (end - p < 18) {

                return NGX_ERROR;

            }

            fmt = rfc822;

        } else if (*p == '-') {

            fmt = rfc850;

        } else {

            return NGX_ERROR;

        }

        p++;

    }

    switch (*p) {

    case 'J':

        month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;

        break;

    case 'F':

        month = 1;

        break;

    case 'M':

        month = *(p + 2) == 'r' ? 2 : 4;

        break;

    case 'A':

        month = *(p + 1) == 'p' ? 3 : 7;

        break;

    case 'S':

        month = 8;

        break;

    case 'O':

        month = 9;

        break;

    case 'N':

        month = 10;

        break;

    case 'D':

        month = 11;

        break;

    default:

        return NGX_ERROR;

    }

    p += 3;

    if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {

        return NGX_ERROR;

    }

    p++;

    if (fmt == rfc822) {

        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'

            || *(p + 2) < '0' || *(p + 2) > '9'

            || *(p + 3) < '0' || *(p + 3) > '9')

        {

            return NGX_ERROR;

        }

        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100

               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';

        p += 4;

    } else if (fmt == rfc850) {

        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {

            return NGX_ERROR;

        }

        year = (*p - '0') * 10 + *(p + 1) - '0';

        year += (year < 70) ? 2000 : 1900;

        p += 2;

    }

    if (fmt == isoc) {

        if (*p == ' ') {

            p++;

        }

        if (*p < '0' || *p > '9') {

            return NGX_ERROR;

        }

        day = *p++ - '0';

        if (*p != ' ') {

            if (*p < '0' || *p > '9') {

                return NGX_ERROR;

            }

            day = day * 10 + *p++ - '0';

        }

        if (end - p < 14) {

            return NGX_ERROR;

        }

    }

    if (*p++ != ' ') {

        return NGX_ERROR;

    }

    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {

        return NGX_ERROR;

    }

    hour = (*p - '0') * 10 + *(p + 1) - '0';

    p += 2;

    if (*p++ != ':') {

        return NGX_ERROR;

    }

    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {

        return NGX_ERROR;

    }

    min = (*p - '0') * 10 + *(p + 1) - '0';

    p += 2;

    if (*p++ != ':') {

        return NGX_ERROR;

    }

    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {

        return NGX_ERROR;

    }

    sec = (*p - '0') * 10 + *(p + 1) - '0';

    if (fmt == isoc) {

        p += 2;

        if (*p++ != ' ') {

            return NGX_ERROR;

        }

        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'

            || *(p + 2) < '0' || *(p + 2) > '9'

            || *(p + 3) < '0' || *(p + 3) > '9')

        {

            return NGX_ERROR;

        }

        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100

               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';

    }

    if (hour > 23 || min > 59 || sec > 59) {

         return NGX_ERROR;

    }

    if (day == 29 && month == 1) {

        if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {

            return NGX_ERROR;

        }

    } else if (day > mday[month]) {

        return NGX_ERROR;

    }

    /*

     * shift new year to March 1 and start months from 1 (not 0),

     * it is needed for Gauss' formula

     */

    if (--month <= 0) {

        month += 12;

        year -= 1;

    }

    /* Gauss' formula for Gregorian days since March 1, 1 BC */

    time = (uint64_t) (

            /* days in years including leap years since March 1, 1 BC */

            365 * year + year / 4 - year / 100 + year / 400

            /* days before the month */

            + 367 * month / 12 - 30

            /* days before the day */

            + day - 1

            /*

             * 719527 days were between March 1, 1 BC and March 1, 1970,

             * 31 and 28 days were in January and February 1970

             */

            - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;

#if (NGX_TIME_T_SIZE <= 4)

    if (time > 0x7fffffff) {

        return NGX_ERROR;

    }

#endif

    return (time_t) time;

}

src/http/ngx_http_upstream.c - nginx-1.7.9

Global variables defined

ngx_http_upstream_cache_method_mask

ngx_http_upstream_commands

ngx_http_upstream_headers_in

ngx_http_upstream_ignore_headers_masks

ngx_http_upstream_module

ngx_http_upstream_module_ctx

ngx_http_upstream_next_errors

ngx_http_upstream_vars

Functions defined

ngx_http_upstream

ngx_http_upstream_add

ngx_http_upstream_add_variables

ngx_http_upstream_addr_variable

ngx_http_upstream_bind_set_slot

ngx_http_upstream_cache

ngx_http_upstream_cache_etag

ngx_http_upstream_cache_get

ngx_http_upstream_cache_last_modified

ngx_http_upstream_cache_send

ngx_http_upstream_cache_status

ngx_http_upstream_check_broken_connection

ngx_http_upstream_cleanup

ngx_http_upstream_connect

ngx_http_upstream_cookie_variable

ngx_http_upstream_copy_allow_ranges

ngx_http_upstream_copy_content_encoding

ngx_http_upstream_copy_content_type

ngx_http_upstream_copy_header_line

ngx_http_upstream_copy_last_modified

ngx_http_upstream_copy_multi_header_lines

ngx_http_upstream_create

ngx_http_upstream_create_main_conf

ngx_http_upstream_dummy_handler

ngx_http_upstream_finalize_request

ngx_http_upstream_get_local

ngx_http_upstream_handler

ngx_http_upstream_header_variable

ngx_http_upstream_hide_headers_hash

ngx_http_upstream_ignore_header_line

ngx_http_upstream_init

ngx_http_upstream_init_main_conf

ngx_http_upstream_init_request

ngx_http_upstream_intercept_errors

ngx_http_upstream_next

ngx_http_upstream_non_buffered_filter

ngx_http_upstream_non_buffered_filter_init

ngx_http_upstream_param_set_slot

ngx_http_upstream_process_accel_expires

ngx_http_upstream_process_body_in_memory

ngx_http_upstream_process_buffering

ngx_http_upstream_process_cache_control

ngx_http_upstream_process_charset

ngx_http_upstream_process_connection

ngx_http_upstream_process_content_length

ngx_http_upstream_process_downstream

ngx_http_upstream_process_expires

ngx_http_upstream_process_header

ngx_http_upstream_process_header_line

ngx_http_upstream_process_headers

ngx_http_upstream_process_last_modified

ngx_http_upstream_process_limit_rate

ngx_http_upstream_process_non_buffered_downstream

ngx_http_upstream_process_non_buffered_request

ngx_http_upstream_process_non_buffered_upstream

ngx_http_upstream_process_request

ngx_http_upstream_process_set_cookie

ngx_http_upstream_process_transfer_encoding

ngx_http_upstream_process_upgraded

ngx_http_upstream_process_upstream

ngx_http_upstream_process_vary

ngx_http_upstream_rd_check_broken_connection

ngx_http_upstream_reinit

ngx_http_upstream_resolve_handler

ngx_http_upstream_response_length_variable

ngx_http_upstream_response_time_variable

ngx_http_upstream_rewrite_location

ngx_http_upstream_rewrite_refresh

ngx_http_upstream_rewrite_set_cookie

ngx_http_upstream_send_request

ngx_http_upstream_send_request_handler

ngx_http_upstream_send_response

ngx_http_upstream_server

ngx_http_upstream_ssl_handshake

ngx_http_upstream_ssl_init_connection

ngx_http_upstream_ssl_name

ngx_http_upstream_status_variable

ngx_http_upstream_store

ngx_http_upstream_test_connect

ngx_http_upstream_test_next

ngx_http_upstream_upgrade

ngx_http_upstream_upgraded_read_downstream

ngx_http_upstream_upgraded_read_upstream

ngx_http_upstream_upgraded_write_downstream

ngx_http_upstream_upgraded_write_upstream

ngx_http_upstream_wr_check_broken_connection

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#if (NGX_HTTP_CACHE)

static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_http_file_cache_t **cache);

static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

#endif

static void ngx_http_upstream_init_request(ngx_http_request_t *r);

static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);

static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);

static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);

static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,

    ngx_event_t *ev);

static void ngx_http_upstream_connect(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_send_request(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_process_header(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);

static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_send_response(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_upgrade(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);

static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);

static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,

    ngx_uint_t from_upstream, ngx_uint_t do_write);

static void

    ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);

static void

    ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void

    ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,

    ngx_uint_t do_write);

static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);

static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,

    ssize_t bytes);

static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);

static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_process_request(ngx_http_request_t *r);

static void ngx_http_upstream_store(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

static void ngx_http_upstream_next(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_uint_t ft_type);

static void ngx_http_upstream_cleanup(void *data);

static void ngx_http_upstream_finalize_request(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_int_t rc);

static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t

    ngx_http_upstream_process_cache_control(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t

    ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_process_vary(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t

    ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

#if (NGX_HTTP_GZIP)

static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset);

#endif

static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);

static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_upstream_response_length_variable(

    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);

static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);

static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r,

    ngx_http_upstream_local_t *local);

static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);

static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);

#if (NGX_HTTP_SSL)

static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,

    ngx_http_upstream_t *u, ngx_connection_t *c);

static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);

static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_connection_t *c);

#endif

‌ngx_http_upstream_header_t  ngx_http_upstream_headers_in[] = {

    { ngx_string("Status"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, status),

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("Content-Type"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, content_type),

                 ngx_http_upstream_copy_content_type, 0, 1 },

    { ngx_string("Content-Length"),

                 ngx_http_upstream_process_content_length, 0,

                 ngx_http_upstream_ignore_header_line, 0, 0 },

    { ngx_string("Date"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, date),

                 ngx_http_upstream_copy_header_line,

                 offsetof(ngx_http_headers_out_t, date), 0 },

    { ngx_string("Last-Modified"),

                 ngx_http_upstream_process_last_modified, 0,

                 ngx_http_upstream_copy_last_modified, 0, 0 },

    { ngx_string("ETag"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, etag),

                 ngx_http_upstream_copy_header_line,

                 offsetof(ngx_http_headers_out_t, etag), 0 },

    { ngx_string("Server"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, server),

                 ngx_http_upstream_copy_header_line,

                 offsetof(ngx_http_headers_out_t, server), 0 },

    { ngx_string("WWW-Authenticate"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, www_authenticate),

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("Location"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, location),

                 ngx_http_upstream_rewrite_location, 0, 0 },

    { ngx_string("Refresh"),

                 ngx_http_upstream_ignore_header_line, 0,

                 ngx_http_upstream_rewrite_refresh, 0, 0 },

    { ngx_string("Set-Cookie"),

                 ngx_http_upstream_process_set_cookie,

                 offsetof(ngx_http_upstream_headers_in_t, cookies),

                 ngx_http_upstream_rewrite_set_cookie, 0, 1 },

    { ngx_string("Content-Disposition"),

                 ngx_http_upstream_ignore_header_line, 0,

                 ngx_http_upstream_copy_header_line, 0, 1 },

    { ngx_string("Cache-Control"),

                 ngx_http_upstream_process_cache_control, 0,

                 ngx_http_upstream_copy_multi_header_lines,

                 offsetof(ngx_http_headers_out_t, cache_control), 1 },

    { ngx_string("Expires"),

                 ngx_http_upstream_process_expires, 0,

                 ngx_http_upstream_copy_header_line,

                 offsetof(ngx_http_headers_out_t, expires), 1 },

    { ngx_string("Accept-Ranges"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, accept_ranges),

                 ngx_http_upstream_copy_allow_ranges,

                 offsetof(ngx_http_headers_out_t, accept_ranges), 1 },

    { ngx_string("Connection"),

                 ngx_http_upstream_process_connection, 0,

                 ngx_http_upstream_ignore_header_line, 0, 0 },

    { ngx_string("Keep-Alive"),

                 ngx_http_upstream_ignore_header_line, 0,

                 ngx_http_upstream_ignore_header_line, 0, 0 },

    { ngx_string("Vary"),

                 ngx_http_upstream_process_vary, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Powered-By"),

                 ngx_http_upstream_ignore_header_line, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Accel-Expires"),

                 ngx_http_upstream_process_accel_expires, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Accel-Redirect"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Accel-Limit-Rate"),

                 ngx_http_upstream_process_limit_rate, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Accel-Buffering"),

                 ngx_http_upstream_process_buffering, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("X-Accel-Charset"),

                 ngx_http_upstream_process_charset, 0,

                 ngx_http_upstream_copy_header_line, 0, 0 },

    { ngx_string("Transfer-Encoding"),

                 ngx_http_upstream_process_transfer_encoding, 0,

                 ngx_http_upstream_ignore_header_line, 0, 0 },

#if (NGX_HTTP_GZIP)

    { ngx_string("Content-Encoding"),

                 ngx_http_upstream_process_header_line,

                 offsetof(ngx_http_upstream_headers_in_t, content_encoding),

                 ngx_http_upstream_copy_content_encoding, 0, 0 },

#endif

    { ngx_null_string, NULL, 0, NULL, 0, 0 }

};

‌static ngx_command_t  ngx_http_upstream_commands[] = {

    { ngx_string("upstream"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,

      ngx_http_upstream,

      0,

      0,

      NULL },

    { ngx_string("server"),

      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,

      ngx_http_upstream_server,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_upstream_module_ctx = {

    ngx_http_upstream_add_variables,       /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_upstream_create_main_conf,    /* create main configuration */

    ngx_http_upstream_init_main_conf,      /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_upstream_module = {

    NGX_MODULE_V1,

    &ngx_http_upstream_module_ctx,         /* module context */

    ngx_http_upstream_commands,            /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_variable_t  ngx_http_upstream_vars[] = {

    { ngx_string("upstream_addr"), NULL,

      ngx_http_upstream_addr_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("upstream_status"), NULL,

      ngx_http_upstream_status_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("upstream_response_time"), NULL,

      ngx_http_upstream_response_time_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("upstream_response_length"), NULL,

      ngx_http_upstream_response_length_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

#if (NGX_HTTP_CACHE)

    { ngx_string("upstream_cache_status"), NULL,

      ngx_http_upstream_cache_status, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("upstream_cache_last_modified"), NULL,

      ngx_http_upstream_cache_last_modified, 0,

      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("upstream_cache_etag"), NULL,

      ngx_http_upstream_cache_etag, 0,

      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

#endif

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌static ngx_http_upstream_next_t  ngx_http_upstream_next_errors[] = {

    { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },

    { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },

    { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },

    { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },

    { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },

    { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { 0, 0 }

};

‌ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[] = {

   { ngx_string("GET"),  NGX_HTTP_GET},

   { ngx_string("HEAD"), NGX_HTTP_HEAD },

   { ngx_string("POST"), NGX_HTTP_POST },

   { ngx_null_string, 0 }

};

‌ngx_conf_bitmask_t  ngx_http_upstream_ignore_headers_masks[] = {

    { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },

    { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },

    { ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE },

    { ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING },

    { ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET },

    { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },

    { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },

    { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE },

    { ngx_string("Vary"), NGX_HTTP_UPSTREAM_IGN_VARY },

    { ngx_null_string, 0 }

};

ngx_int_t

‌ngx_http_upstream_create(ngx_http_request_t *r)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    if (u && u->cleanup) {

        r->main->count++;

        ngx_http_upstream_cleanup(r);

    }

    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));

    if (u == NULL) {

        return NGX_ERROR;

    }

    r->upstream = u;

    u->peer.log = r->connection->log;

    u->peer.log_error = NGX_ERROR_ERR;

#if (NGX_THREADS)

    u->peer.lock = &r->connection->lock;

#endif

#if (NGX_HTTP_CACHE)

    r->cache = NULL;

#endif

    u->headers_in.content_length_n = -1;

    u->headers_in.last_modified_time = -1;

    return NGX_OK;

}

void

‌ngx_http_upstream_init(ngx_http_request_t *r)

{

    ngx_connection_t     *c;

    c = r->connection;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http init upstream, client timer: %d", c->read->timer_set);

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        ngx_http_upstream_init_request(r);

        return;

    }

#endif

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        if (!c->write->active) {

            if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)

                == NGX_ERROR)

            {

                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

        }

    }

    ngx_http_upstream_init_request(r);

}

static void

‌ngx_http_upstream_init_request(ngx_http_request_t *r)

{

    ngx_str_t                      *host;

    ngx_uint_t                      i;

    ngx_resolver_ctx_t             *ctx, temp;

    ngx_http_cleanup_t             *cln;

    ngx_http_upstream_t            *u;

    ngx_http_core_loc_conf_t       *clcf;

    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;

    ngx_http_upstream_main_conf_t  *umcf;

    if (r->aio) {

        return;

    }

    u = r->upstream;

#if (NGX_HTTP_CACHE)

    if (u->conf->cache) {

        ngx_int_t  rc;

        rc = ngx_http_upstream_cache(r, u);

        if (rc == NGX_BUSY) {

            r->write_event_handler = ngx_http_upstream_init_request;

            return;

        }

        r->write_event_handler = ngx_http_request_empty_handler;

        if (rc == NGX_DONE) {

            return;

        }

        if (rc == NGX_ERROR) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        if (rc != NGX_DECLINED) {

            ngx_http_finalize_request(r, rc);

            return;

        }

    }

#endif

    u->store = u->conf->store;

    if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {

        r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;

        r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;

    }

    if (r->request_body) {

        u->request_bufs = r->request_body->bufs;

    }

    if (u->create_request(r) != NGX_OK) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    u->peer.local = ngx_http_upstream_get_local(r, u->conf->local);

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    u->output.alignment = clcf->directio_alignment;

    u->output.pool = r->pool;

    u->output.bufs.num = 1;

    u->output.bufs.size = clcf->client_body_buffer_size;

    u->output.output_filter = ngx_chain_writer;

    u->output.filter_ctx = &u->writer;

    u->writer.pool = r->pool;

    if (r->upstream_states == NULL) {

        r->upstream_states = ngx_array_create(r->pool, 1,

                                            sizeof(ngx_http_upstream_state_t));

        if (r->upstream_states == NULL) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    } else {

        u->state = ngx_array_push(r->upstream_states);

        if (u->state == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));

    }

    cln = ngx_http_cleanup_add(r, 0);

    if (cln == NULL) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    cln->handler = ngx_http_upstream_cleanup;

    cln->data = r;

    u->cleanup = &cln->handler;

    if (u->resolved == NULL) {

        uscf = u->conf->upstream;

    } else {

#if (NGX_HTTP_SSL)

        u->ssl_name = u->resolved->host;

#endif

        if (u->resolved->sockaddr) {

            if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)

                != NGX_OK)

            {

                ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            ngx_http_upstream_connect(r, u);

            return;

        }

        host = &u->resolved->host;

        umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

        uscfp = umcf->upstreams.elts;

        for (i = 0; i < umcf->upstreams.nelts; i++) {

            uscf = uscfp[i];

            if (uscf->host.len == host->len

                && ((uscf->port == 0 && u->resolved->no_port)

                     || uscf->port == u->resolved->port)

                && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)

            {

                goto found;

            }

        }

        if (u->resolved->port == 0) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "no port in upstream \"%V\"", host);

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        temp.name = *host;

        ctx = ngx_resolve_start(clcf->resolver, &temp);

        if (ctx == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        if (ctx == NGX_NO_RESOLVER) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "no resolver defined to resolve %V", host);

            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);

            return;

        }

        ctx->name = *host;

        ctx->handler = ngx_http_upstream_resolve_handler;

        ctx->data = r;

        ctx->timeout = clcf->resolver_timeout;

        u->resolved->ctx = ctx;

        if (ngx_resolve_name(ctx) != NGX_OK) {

            u->resolved->ctx = NULL;

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        return;

    }

found:

    if (uscf == NULL) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "no upstream configuration");

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

#if (NGX_HTTP_SSL)

    u->ssl_name = uscf->host;

#endif

    if (uscf->peer.init(r, uscf) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    u->peer.start_time = ngx_current_msec;

    if (u->conf->next_upstream_tries

        && u->peer.tries > u->conf->next_upstream_tries)

    {

        u->peer.tries = u->conf->next_upstream_tries;

    }

    ngx_http_upstream_connect(r, u);

}

#if (NGX_HTTP_CACHE)

static ngx_int_t

‌ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_int_t               rc;

    ngx_http_cache_t       *c;

    ngx_http_file_cache_t  *cache;

    c = r->cache;

    if (c == NULL) {

        if (!(r->method & u->conf->cache_methods)) {

            return NGX_DECLINED;

        }

        rc = ngx_http_upstream_cache_get(r, u, &cache);

        if (rc != NGX_OK) {

            return rc;

        }

        if (r->method & NGX_HTTP_HEAD) {

            u->method = ngx_http_core_get_method;

        }

        if (ngx_http_file_cache_new(r) != NGX_OK) {

            return NGX_ERROR;

        }

        if (u->create_key(r) != NGX_OK) {

            return NGX_ERROR;

        }

        /* TODO: add keys */

        ngx_http_file_cache_create_key(r);

        if (r->cache->header_start + 256 >= u->conf->buffer_size) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "%V_buffer_size %uz is not enough for cache key, "

                          "it should be increased to at least %uz",

                          &u->conf->module, u->conf->buffer_size,

                          ngx_align(r->cache->header_start + 256, 1024));

            r->cache = NULL;

            return NGX_DECLINED;

        }

        u->cacheable = 1;

        c = r->cache;

        c->body_start = u->conf->buffer_size;

        c->min_uses = u->conf->cache_min_uses;

        c->file_cache = cache;

        switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {

        case NGX_ERROR:

            return NGX_ERROR;

        case NGX_DECLINED:

            u->cache_status = NGX_HTTP_CACHE_BYPASS;

            return NGX_DECLINED;

        default: /* NGX_OK */

            break;

        }

        c->lock = u->conf->cache_lock;

        c->lock_timeout = u->conf->cache_lock_timeout;

        c->lock_age = u->conf->cache_lock_age;

        u->cache_status = NGX_HTTP_CACHE_MISS;

    }

    rc = ngx_http_file_cache_open(r);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http upstream cache: %i", rc);

    switch (rc) {

    case NGX_HTTP_CACHE_UPDATING:

        if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) {

            u->cache_status = rc;

            rc = NGX_OK;

        } else {

            rc = NGX_HTTP_CACHE_STALE;

        }

        break;

    case NGX_OK:

        u->cache_status = NGX_HTTP_CACHE_HIT;

    }

    switch (rc) {

    case NGX_OK:

        rc = ngx_http_upstream_cache_send(r, u);

        if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) {

            return rc;

        }

        break;

    case NGX_HTTP_CACHE_STALE:

        c->valid_sec = 0;

        u->buffer.start = NULL;

        u->cache_status = NGX_HTTP_CACHE_EXPIRED;

        break;

    case NGX_DECLINED:

        if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {

            u->buffer.start = NULL;

        } else {

            u->buffer.pos = u->buffer.start + c->header_start;

            u->buffer.last = u->buffer.pos;

        }

        break;

    case NGX_HTTP_CACHE_SCARCE:

        u->cacheable = 0;

        break;

    case NGX_AGAIN:

        return NGX_BUSY;

    case NGX_ERROR:

        return NGX_ERROR;

    default:

        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */

        u->cache_status = NGX_HTTP_CACHE_HIT;

        return rc;

    }

    r->cached = 0;

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u,

    ngx_http_file_cache_t **cache)

{

    ngx_str_t               *name, val;

    ngx_uint_t               i;

    ngx_http_file_cache_t  **caches;

    if (u->conf->cache_zone) {

        *cache = u->conf->cache_zone->data;

        return NGX_OK;

    }

    if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) {

        return NGX_ERROR;

    }

    if (val.len == 0

        || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0))

    {

        return NGX_DECLINED;

    }

    caches = u->caches->elts;

    for (i = 0; i < u->caches->nelts; i++) {

        name = &caches[i]->shm_zone->shm.name;

        if (name->len == val.len

            && ngx_strncmp(name->data, val.data, val.len) == 0)

        {

            *cache = caches[i];

            return NGX_OK;

        }

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "cache \"%V\" not found", &val);

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_int_t          rc;

    ngx_http_cache_t  *c;

    r->cached = 1;

    c = r->cache;

    if (c->header_start == c->body_start) {

        r->http_version = NGX_HTTP_VERSION_9;

        return ngx_http_cache_send(r);

    }

    /* TODO: cache stack */

    u->buffer = *c->buf;

    u->buffer.pos += c->header_start;

    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));

    u->headers_in.content_length_n = -1;

    u->headers_in.last_modified_time = -1;

    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,

                      sizeof(ngx_table_elt_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    rc = u->process_header(r);

    if (rc == NGX_OK) {

        if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {

            return NGX_DONE;

        }

        return ngx_http_cache_send(r);

    }

    if (rc == NGX_ERROR) {

        return NGX_ERROR;

    }

    /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */

    /* TODO: delete file */

    return rc;

}

#endif

static void

‌ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)

{

    ngx_connection_t              *c;

    ngx_http_request_t            *r;

    ngx_http_upstream_t           *u;

    ngx_http_upstream_resolved_t  *ur;

    r = ctx->data;

    c = r->connection;

    u = r->upstream;

    ur = u->resolved;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream resolve: \"%V?%V\"", &r->uri, &r->args);

    if (ctx->state) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "%V could not be resolved (%i: %s)",

                      &ctx->name, ctx->state,

                      ngx_resolver_strerror(ctx->state));

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);

        goto failed;

    }

    ur->naddrs = ctx->naddrs;

    ur->addrs = ctx->addrs;

#if (NGX_DEBUG)

    {

    u_char      text[NGX_SOCKADDR_STRLEN];

    ngx_str_t   addr;

    ngx_uint_t  i;

    addr.data = text;

    for (i = 0; i < ctx->naddrs; i++) {

        addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,

                                 text, NGX_SOCKADDR_STRLEN, 0);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "name was resolved to %V", &addr);

    }

    }

#endif

    if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        goto failed;

    }

    ngx_resolve_name_done(ctx);

    ur->ctx = NULL;

    u->peer.start_time = ngx_current_msec;

    if (u->conf->next_upstream_tries

        && u->peer.tries > u->conf->next_upstream_tries)

    {

        u->peer.tries = u->conf->next_upstream_tries;

    }

    ngx_http_upstream_connect(r, u);

failed:

    ngx_http_run_posted_requests(c);

}

static void

‌ngx_http_upstream_handler(ngx_event_t *ev)

{

    ngx_connection_t     *c;

    ngx_http_request_t   *r;

    ngx_http_upstream_t  *u;

    c = ev->data;

    r = c->data;

    u = r->upstream;

    c = r->connection;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream request: \"%V?%V\"", &r->uri, &r->args);

    if (ev->write) {

        u->write_event_handler(r, u);

    } else {

        u->read_event_handler(r, u);

    }

    ngx_http_run_posted_requests(c);

}

static void

‌ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)

{

    ngx_http_upstream_check_broken_connection(r, r->connection->read);

}

static void

‌ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r)

{

    ngx_http_upstream_check_broken_connection(r, r->connection->write);

}

static void

‌ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,

    ngx_event_t *ev)

{

    int                  n;

    char                 buf[1];

    ngx_err_t            err;

    ngx_int_t            event;

    ngx_connection_t     *c;

    ngx_http_upstream_t  *u;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,

                   "http upstream check client, write event:%d, \"%V\"",

                   ev->write, &r->uri);

    c = r->connection;

    u = r->upstream;

    if (c->error) {

        if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {

            event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;

            if (ngx_del_event(ev, event, 0) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

        }

        if (!u->cacheable) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);

        }

        return;

    }

#if (NGX_HTTP_SPDY)

    if (r->spdy_stream) {

        return;

    }

#endif

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        if (!ev->pending_eof) {

            return;

        }

        ev->eof = 1;

        c->error = 1;

        if (ev->kq_errno) {

            ev->error = 1;

        }

        if (!u->cacheable && u->peer.connection) {

            ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,

                          "kevent() reported that client prematurely closed "

                          "connection, so upstream connection is closed too");

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);

            return;

        }

        ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,

                      "kevent() reported that client prematurely closed "

                      "connection");

        if (u->peer.connection == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);

        }

        return;

    }

#endif

#if (NGX_HAVE_EPOLLRDHUP)

    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ev->pending_eof) {

        socklen_t  len;

        ev->eof = 1;

        c->error = 1;

        err = 0;

        len = sizeof(ngx_err_t);

        /*

         * BSDs and Linux return 0 and set a pending error in err

         * Solaris returns -1 and sets errno

         */

        if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)

            == -1)

        {

            err = ngx_socket_errno;

        }

        if (err) {

            ev->error = 1;

        }

        if (!u->cacheable && u->peer.connection) {

            ngx_log_error(NGX_LOG_INFO, ev->log, err,

                        "epoll_wait() reported that client prematurely closed "

                        "connection, so upstream connection is closed too");

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);

            return;

        }

        ngx_log_error(NGX_LOG_INFO, ev->log, err,

                      "epoll_wait() reported that client prematurely closed "

                      "connection");

        if (u->peer.connection == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);

        }

        return;

    }

#endif

    n = recv(c->fd, buf, 1, MSG_PEEK);

    err = ngx_socket_errno;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,

                   "http upstream recv(): %d", n);

    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {

        return;

    }

    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {

        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;

        if (ngx_del_event(ev, event, 0) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    }

    if (n > 0) {

        return;

    }

    if (n == -1) {

        if (err == NGX_EAGAIN) {

            return;

        }

        ev->error = 1;

    } else { /* n == 0 */

        err = 0;

    }

    ev->eof = 1;

    c->error = 1;

    if (!u->cacheable && u->peer.connection) {

        ngx_log_error(NGX_LOG_INFO, ev->log, err,

                      "client prematurely closed connection, "

                      "so upstream connection is closed too");

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);

        return;

    }

    ngx_log_error(NGX_LOG_INFO, ev->log, err,

                  "client prematurely closed connection");

    if (u->peer.connection == NULL) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);

    }

}

static void

‌ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_int_t          rc;

    ngx_time_t        *tp;

    ngx_connection_t  *c;

    r->connection->log->action = "connecting to upstream";

    if (u->state && u->state->response_sec) {

        tp = ngx_timeofday();

        u->state->response_sec = tp->sec - u->state->response_sec;

        u->state->response_msec = tp->msec - u->state->response_msec;

    }

    u->state = ngx_array_push(r->upstream_states);

    if (u->state == NULL) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));

    tp = ngx_timeofday();

    u->state->response_sec = tp->sec;

    u->state->response_msec = tp->msec;

    rc = ngx_event_connect_peer(&u->peer);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http upstream connect: %i", rc);

    if (rc == NGX_ERROR) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    u->state->peer = u->peer.name;

    if (rc == NGX_BUSY) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);

        return;

    }

    if (rc == NGX_DECLINED) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

        return;

    }

    /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */

    c = u->peer.connection;

    c->data = r;

    c->write->handler = ngx_http_upstream_handler;

    c->read->handler = ngx_http_upstream_handler;

    u->write_event_handler = ngx_http_upstream_send_request_handler;

    u->read_event_handler = ngx_http_upstream_process_header;

    c->sendfile &= r->connection->sendfile;

    u->output.sendfile = c->sendfile;

    if (c->pool == NULL) {

        /* we need separate pool here to be able to cache SSL connections */

        c->pool = ngx_create_pool(128, r->connection->log);

        if (c->pool == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    }

    c->log = r->connection->log;

    c->pool->log = c->log;

    c->read->log = c->log;

    c->write->log = c->log;

    /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */

    u->writer.out = NULL;

    u->writer.last = &u->writer.out;

    u->writer.connection = c;

    u->writer.limit = 0;

    if (u->request_sent) {

        if (ngx_http_upstream_reinit(r, u) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    }

    if (r->request_body

        && r->request_body->buf

        && r->request_body->temp_file

        && r == r->main)

    {

        /*

         * the r->request_body->buf can be reused for one request only,

         * the subrequests should allocate their own temporary bufs

         */

        u->output.free = ngx_alloc_chain_link(r->pool);

        if (u->output.free == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        u->output.free->buf = r->request_body->buf;

        u->output.free->next = NULL;

        u->output.allocated = 1;

        r->request_body->buf->pos = r->request_body->buf->start;

        r->request_body->buf->last = r->request_body->buf->start;

        r->request_body->buf->tag = u->output.tag;

    }

    u->request_sent = 0;

    if (rc == NGX_AGAIN) {

        ngx_add_timer(c->write, u->conf->connect_timeout);

        return;

    }

#if (NGX_HTTP_SSL)

    if (u->ssl && c->ssl == NULL) {

        ngx_http_upstream_ssl_init_connection(r, u, c);

        return;

    }

#endif

    ngx_http_upstream_send_request(r, u);

}

#if (NGX_HTTP_SSL)

static void

‌ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_connection_t *c)

{

    ngx_int_t   rc;

    if (ngx_http_upstream_test_connect(c) != NGX_OK) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

        return;

    }

    if (ngx_ssl_create_connection(u->conf->ssl, c,

                                  NGX_SSL_BUFFER|NGX_SSL_CLIENT)

        != NGX_OK)

    {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    c->sendfile = 0;

    u->output.sendfile = 0;

    if (u->conf->ssl_server_name || u->conf->ssl_verify) {

        if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    }

    if (u->conf->ssl_session_reuse) {

        if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

    }

    r->connection->log->action = "SSL handshaking to upstream";

    rc = ngx_ssl_handshake(c);

    if (rc == NGX_AGAIN) {

        if (!c->write->timer_set) {

            ngx_add_timer(c->write, u->conf->connect_timeout);

        }

        c->ssl->handler = ngx_http_upstream_ssl_handshake;

        return;

    }

    ngx_http_upstream_ssl_handshake(c);

}

static void

‌ngx_http_upstream_ssl_handshake(ngx_connection_t *c)

{

    long                  rc;

    ngx_http_request_t   *r;

    ngx_http_upstream_t  *u;

    r = c->data;

    u = r->upstream;

    ngx_http_set_log_request(c->log, r);

    if (c->ssl->handshaked) {

        if (u->conf->ssl_verify) {

            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK) {

                ngx_log_error(NGX_LOG_ERR, c->log, 0,

                              "upstream SSL certificate verify error: (%l:%s)",

                              rc, X509_verify_cert_error_string(rc));

                goto failed;

            }

            if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {

                ngx_log_error(NGX_LOG_ERR, c->log, 0,

                              "upstream SSL certificate does not match \"%V\"",

                              &u->ssl_name);

                goto failed;

            }

        }

        if (u->conf->ssl_session_reuse) {

            u->peer.save_session(&u->peer, u->peer.data);

        }

        c->write->handler = ngx_http_upstream_handler;

        c->read->handler = ngx_http_upstream_handler;

        c = r->connection;

        ngx_http_upstream_send_request(r, u);

        ngx_http_run_posted_requests(c);

        return;

    }

failed:

    c = r->connection;

    ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

    ngx_http_run_posted_requests(c);

}

static ngx_int_t

‌ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u,

    ngx_connection_t *c)

{

    u_char     *p, *last;

    ngx_str_t   name;

    if (u->conf->ssl_name) {

        if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) {

            return NGX_ERROR;

        }

    } else {

        name = u->ssl_name;

    }

    if (name.len == 0) {

        goto done;

    }

    /*

     * ssl name here may contain port, notably if derived from $proxy_host

     * or $http_host; we have to strip it

     */

    p = name.data;

    last = name.data + name.len;

    if (*p == '[') {

        p = ngx_strlchr(p, last, ']');

        if (p == NULL) {

            p = name.data;

        }

    }

    p = ngx_strlchr(p, last, ':');

    if (p != NULL) {

        name.len = p - name.data;

    }

    if (!u->conf->ssl_server_name) {

        goto done;

    }

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

    /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */

    if (name.len == 0 || *name.data == '[') {

        goto done;

    }

    if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {

        goto done;

    }

    /*

     * SSL_set_tlsext_host_name() needs a null-terminated string,

     * hence we explicitly null-terminate name here

     */

    p = ngx_pnalloc(r->pool, name.len + 1);

    if (p == NULL) {

        return NGX_ERROR;

    }

    (void) ngx_cpystrn(p, name.data, name.len + 1);

    name.data = p;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "upstream SSL server name: \"%s\"", name.data);

    if (SSL_set_tlsext_host_name(c->ssl->connection, name.data) == 0) {

        ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0,

                      "SSL_set_tlsext_host_name(\"%s\") failed", name.data);

        return NGX_ERROR;

    }

#endif

done:

    u->ssl_name = name;

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    off_t         file_pos;

    ngx_chain_t  *cl;

    if (u->reinit_request(r) != NGX_OK) {

        return NGX_ERROR;

    }

    u->keepalive = 0;

    u->upgrade = 0;

    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));

    u->headers_in.content_length_n = -1;

    u->headers_in.last_modified_time = -1;

    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,

                      sizeof(ngx_table_elt_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    /* reinit the request chain */

    file_pos = 0;

    for (cl = u->request_bufs; cl; cl = cl->next) {

        cl->buf->pos = cl->buf->start;

        /* there is at most one file */

        if (cl->buf->in_file) {

            cl->buf->file_pos = file_pos;

            file_pos = cl->buf->file_last;

        }

    }

    /* reinit the subrequest's ngx_output_chain() context */

    if (r->request_body && r->request_body->temp_file

        && r != r->main && u->output.buf)

    {

        u->output.free = ngx_alloc_chain_link(r->pool);

        if (u->output.free == NULL) {

            return NGX_ERROR;

        }

        u->output.free->buf = u->output.buf;

        u->output.free->next = NULL;

        u->output.buf->pos = u->output.buf->start;

        u->output.buf->last = u->output.buf->start;

    }

    u->output.buf = NULL;

    u->output.in = NULL;

    u->output.busy = NULL;

    /* reinit u->buffer */

    u->buffer.pos = u->buffer.start;

#if (NGX_HTTP_CACHE)

    if (r->cache) {

        u->buffer.pos += r->cache->header_start;

    }

#endif

    u->buffer.last = u->buffer.pos;

    return NGX_OK;

}

static void

‌ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_int_t          rc;

    ngx_connection_t  *c;

    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream send request");

    if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

        return;

    }

    c->log->action = "sending request to upstream";

    rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs);

    u->request_sent = 1;

    if (rc == NGX_ERROR) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

        return;

    }

    if (c->write->timer_set) {

        ngx_del_timer(c->write);

    }

    if (rc == NGX_AGAIN) {

        ngx_add_timer(c->write, u->conf->send_timeout);

        if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        return;

    }

    /* rc == NGX_OK */

    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {

        if (ngx_tcp_push(c->fd) == NGX_ERROR) {

            ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,

                          ngx_tcp_push_n " failed");

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;

    }

    u->write_event_handler = ngx_http_upstream_dummy_handler;

    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    ngx_add_timer(c->read, u->conf->read_timeout);

    if (c->read->ready) {

        ngx_http_upstream_process_header(r, u);

        return;

    }

}

static void

‌ngx_http_upstream_send_request_handler(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_connection_t  *c;

    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http upstream send request handler");

    if (c->write->timedout) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);

        return;

    }

#if (NGX_HTTP_SSL)

    if (u->ssl && c->ssl == NULL) {

        ngx_http_upstream_ssl_init_connection(r, u, c);

        return;

    }

#endif

    if (u->header_sent) {

        u->write_event_handler = ngx_http_upstream_dummy_handler;

        (void) ngx_handle_write_event(c->write, 0);

        return;

    }

    ngx_http_upstream_send_request(r, u);

}

static void

‌ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ssize_t            n;

    ngx_int_t          rc;

    ngx_connection_t  *c;

    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process header");

    c->log->action = "reading response header from upstream";

    if (c->read->timedout) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);

        return;

    }

    if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

        return;

    }

    if (u->buffer.start == NULL) {

        u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);

        if (u->buffer.start == NULL) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        u->buffer.pos = u->buffer.start;

        u->buffer.last = u->buffer.start;

        u->buffer.end = u->buffer.start + u->conf->buffer_size;

        u->buffer.temporary = 1;

        u->buffer.tag = u->output.tag;

        if (ngx_list_init(&u->headers_in.headers, r->pool, 8,

                          sizeof(ngx_table_elt_t))

            != NGX_OK)

        {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

#if (NGX_HTTP_CACHE)

        if (r->cache) {

            u->buffer.pos += r->cache->header_start;

            u->buffer.last = u->buffer.pos;

        }

#endif

    }

    for ( ;; ) {

        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);

        if (n == NGX_AGAIN) {

#if 0

            ngx_add_timer(rev, u->read_timeout);

#endif

            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                return;

            }

            return;

        }

        if (n == 0) {

            ngx_log_error(NGX_LOG_ERR, c->log, 0,

                          "upstream prematurely closed connection");

        }

        if (n == NGX_ERROR || n == 0) {

            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

            return;

        }

        u->buffer.last += n;

#if 0

        u->valid_header_in = 0;

        u->peer.cached = 0;

#endif

        rc = u->process_header(r);

        if (rc == NGX_AGAIN) {

            if (u->buffer.last == u->buffer.end) {

                ngx_log_error(NGX_LOG_ERR, c->log, 0,

                              "upstream sent too big header");

                ngx_http_upstream_next(r, u,

                                       NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);

                return;

            }

            continue;

        }

        break;

    }

    if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {

        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);

        return;

    }

    if (rc == NGX_ERROR) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    /* rc == NGX_OK */

    if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {

        if (ngx_http_upstream_test_next(r, u) == NGX_OK) {

            return;

        }

        if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) {

            return;

        }

    }

    if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {

        return;

    }

    if (!r->subrequest_in_memory) {

        ngx_http_upstream_send_response(r, u);

        return;

    }

    /* subrequest content in memory */

    if (u->input_filter == NULL) {

        u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;

        u->input_filter = ngx_http_upstream_non_buffered_filter;

        u->input_filter_ctx = r;

    }

    if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    n = u->buffer.last - u->buffer.pos;

    if (n) {

        u->buffer.last = u->buffer.pos;

        u->state->response_length += n;

        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

    }

    if (u->length == 0) {

        ngx_http_upstream_finalize_request(r, u, 0);

        return;

    }

    u->read_event_handler = ngx_http_upstream_process_body_in_memory;

    ngx_http_upstream_process_body_in_memory(r, u);

}

static ngx_int_t

‌ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_uint_t                 status;

    ngx_http_upstream_next_t  *un;

    status = u->headers_in.status_n;

    for (un = ngx_http_upstream_next_errors; un->status; un++) {

        if (status != un->status) {

            continue;

        }

        if (u->peer.tries > 1 && (u->conf->next_upstream & un->mask)) {

            ngx_http_upstream_next(r, u, un->mask);

            return NGX_OK;

        }

#if (NGX_HTTP_CACHE)

        if (u->cache_status == NGX_HTTP_CACHE_EXPIRED

            && (u->conf->cache_use_stale & un->mask))

        {

            ngx_int_t  rc;

            rc = u->reinit_request(r);

            if (rc == NGX_OK) {

                u->cache_status = NGX_HTTP_CACHE_STALE;

                rc = ngx_http_upstream_cache_send(r, u);

            }

            ngx_http_upstream_finalize_request(r, u, rc);

            return NGX_OK;

        }

#endif

    }

#if (NGX_HTTP_CACHE)

    if (status == NGX_HTTP_NOT_MODIFIED

        && u->cache_status == NGX_HTTP_CACHE_EXPIRED

        && u->conf->cache_revalidate)

    {

        time_t     now, valid;

        ngx_int_t  rc;

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http upstream not modified");

        now = ngx_time();

        valid = r->cache->valid_sec;

        rc = u->reinit_request(r);

        if (rc != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u, rc);

            return NGX_OK;

        }

        u->cache_status = NGX_HTTP_CACHE_REVALIDATED;

        rc = ngx_http_upstream_cache_send(r, u);

        if (valid == 0) {

            valid = r->cache->valid_sec;

        }

        if (valid == 0) {

            valid = ngx_http_file_cache_valid(u->conf->cache_valid,

                                              u->headers_in.status_n);

            if (valid) {

                valid = now + valid;

            }

        }

        if (valid) {

            r->cache->valid_sec = valid;

            r->cache->date = now;

            ngx_http_file_cache_update_header(r);

        }

        ngx_http_upstream_finalize_request(r, u, rc);

        return NGX_OK;

    }

#endif

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_upstream_intercept_errors(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_int_t                  status;

    ngx_uint_t                 i;

    ngx_table_elt_t           *h;

    ngx_http_err_page_t       *err_page;

    ngx_http_core_loc_conf_t  *clcf;

    status = u->headers_in.status_n;

    if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);

        return NGX_OK;

    }

    if (!u->conf->intercept_errors) {

        return NGX_DECLINED;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->error_pages == NULL) {

        return NGX_DECLINED;

    }

    err_page = clcf->error_pages->elts;

    for (i = 0; i < clcf->error_pages->nelts; i++) {

        if (err_page[i].status == status) {

            if (status == NGX_HTTP_UNAUTHORIZED

                && u->headers_in.www_authenticate)

            {

                h = ngx_list_push(&r->headers_out.headers);

                if (h == NULL) {

                    ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                    return NGX_OK;

                }

                *h = *u->headers_in.www_authenticate;

                r->headers_out.www_authenticate = h;

            }

#if (NGX_HTTP_CACHE)

            if (r->cache) {

                time_t  valid;

                valid = ngx_http_file_cache_valid(u->conf->cache_valid, status);

                if (valid) {

                    r->cache->valid_sec = ngx_time() + valid;

                    r->cache->error = status;

                }

                ngx_http_file_cache_free(r->cache, u->pipe->temp_file);

            }

#endif

            ngx_http_upstream_finalize_request(r, u, status);

            return NGX_OK;

        }

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_upstream_test_connect(ngx_connection_t *c)

{

    int        err;

    socklen_t  len;

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {

        if (c->write->pending_eof || c->read->pending_eof) {

            if (c->write->pending_eof) {

                err = c->write->kq_errno;

            } else {

                err = c->read->kq_errno;

            }

            c->log->action = "connecting to upstream";

            (void) ngx_connection_error(c, err,

                                    "kevent() reported that connect() failed");

            return NGX_ERROR;

        }

    } else

#endif

    {

        err = 0;

        len = sizeof(int);

        /*

         * BSDs and Linux return 0 and set a pending error in err

         * Solaris returns -1 and sets errno

         */

        if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)

            == -1)

        {

            err = ngx_socket_errno;

        }

        if (err) {

            c->log->action = "connecting to upstream";

            (void) ngx_connection_error(c, err, "connect() failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_str_t                       uri, args;

    ngx_uint_t                      i, flags;

    ngx_list_part_t                *part;

    ngx_table_elt_t                *h;

    ngx_http_upstream_header_t     *hh;

    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    if (u->headers_in.x_accel_redirect

        && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))

    {

        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);

        part = &u->headers_in.headers.part;

        h = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                h = part->elts;

                i = 0;

            }

            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,

                               h[i].lowcase_key, h[i].key.len);

            if (hh && hh->redirect) {

                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {

                    ngx_http_finalize_request(r,

                                              NGX_HTTP_INTERNAL_SERVER_ERROR);

                    return NGX_DONE;

                }

            }

        }

        uri = u->headers_in.x_accel_redirect->value;

        if (uri.data[0] == '@') {

            ngx_http_named_location(r, &uri);

        } else {

            ngx_str_null(&args);

            flags = NGX_HTTP_LOG_UNSAFE;

            if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {

                ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);

                return NGX_DONE;

            }

            if (r->method != NGX_HTTP_HEAD) {

                r->method = NGX_HTTP_GET;

            }

            ngx_http_internal_redirect(r, &uri, &args);

        }

        ngx_http_finalize_request(r, NGX_DONE);

        return NGX_DONE;

    }

    part = &u->headers_in.headers.part;

    h = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            h = part->elts;

            i = 0;

        }

        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,

                          h[i].lowcase_key, h[i].key.len))

        {

            continue;

        }

        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,

                           h[i].lowcase_key, h[i].key.len);

        if (hh) {

            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

                return NGX_DONE;

            }

            continue;

        }

        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {

            ngx_http_upstream_finalize_request(r, u,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

            return NGX_DONE;

        }

    }

    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {

        r->headers_out.server->hash = 0;

    }

    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {

        r->headers_out.date->hash = 0;

    }

    r->headers_out.status = u->headers_in.status_n;

    r->headers_out.status_line = u->headers_in.status_line;

    r->headers_out.content_length_n = u->headers_in.content_length_n;

    r->disable_not_modified = !u->cacheable;

    if (u->conf->force_ranges) {

        r->allow_ranges = 1;

        r->single_range = 1;

#if (NGX_HTTP_CACHE)

        if (r->cached) {

            r->single_range = 0;

        }

#endif

    }

    u->length = -1;

    return NGX_OK;

}

static void

‌ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    size_t             size;

    ssize_t            n;

    ngx_buf_t         *b;

    ngx_event_t       *rev;

    ngx_connection_t  *c;

    c = u->peer.connection;

    rev = c->read;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process body on memory");

    if (rev->timedout) {

        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);

        return;

    }

    b = &u->buffer;

    for ( ;; ) {

        size = b->end - b->last;

        if (size == 0) {

            ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                          "upstream buffer is too small to read response");

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

        n = c->recv(c, b->last, size);

        if (n == NGX_AGAIN) {

            break;

        }

        if (n == 0 || n == NGX_ERROR) {

            ngx_http_upstream_finalize_request(r, u, n);

            return;

        }

        u->state->response_length += n;

        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

        if (!rev->ready) {

            break;

        }

    }

    if (u->length == 0) {

        ngx_http_upstream_finalize_request(r, u, 0);

        return;

    }

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (rev->active) {

        ngx_add_timer(rev, u->conf->read_timeout);

    } else if (rev->timer_set) {

        ngx_del_timer(rev);

    }

}

static void

‌ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    int                        tcp_nodelay;

    ssize_t                    n;

    ngx_int_t                  rc;

    ngx_event_pipe_t          *p;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {

        ngx_http_upstream_finalize_request(r, u, rc);

        return;

    }

    u->header_sent = 1;

    if (u->upgrade) {

        ngx_http_upstream_upgrade(r, u);

        return;

    }

    c = r->connection;

    if (r->header_only) {

        if (!u->buffering) {

            ngx_http_upstream_finalize_request(r, u, rc);

            return;

        }

        if (!u->cacheable && !u->store) {

            ngx_http_upstream_finalize_request(r, u, rc);

            return;

        }

        u->pipe->downstream_error = 1;

    }

    if (r->request_body && r->request_body->temp_file) {

        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);

        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!u->buffering) {

        if (u->input_filter == NULL) {

            u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;

            u->input_filter = ngx_http_upstream_non_buffered_filter;

            u->input_filter_ctx = r;

        }

        u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;

        r->write_event_handler =

                             ngx_http_upstream_process_non_buffered_downstream;

        r->limit_rate = 0;

        if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

        if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");

            tcp_nodelay = 1;

            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,

                               (const void *) &tcp_nodelay, sizeof(int)) == -1)

            {

                ngx_connection_error(c, ngx_socket_errno,

                                     "setsockopt(TCP_NODELAY) failed");

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            c->tcp_nodelay = NGX_TCP_NODELAY_SET;

        }

        n = u->buffer.last - u->buffer.pos;

        if (n) {

            u->buffer.last = u->buffer.pos;

            u->state->response_length += n;

            if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            ngx_http_upstream_process_non_buffered_downstream(r);

        } else {

            u->buffer.pos = u->buffer.start;

            u->buffer.last = u->buffer.start;

            if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            if (u->peer.connection->read->ready || u->length == 0) {

                ngx_http_upstream_process_non_buffered_upstream(r, u);

            }

        }

        return;

    }

    /* TODO: preallocate event_pipe bufs, look "Content-Length" */

#if (NGX_HTTP_CACHE)

    if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {

        ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);

        r->cache->file.fd = NGX_INVALID_FILE;

    }

    switch (ngx_http_test_predicates(r, u->conf->no_cache)) {

    case NGX_ERROR:

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    case NGX_DECLINED:

        u->cacheable = 0;

        break;

    default: /* NGX_OK */

        if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {

            /* create cache if previously bypassed */

            if (ngx_http_file_cache_create(r) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

        }

        break;

    }

    if (u->cacheable) {

        time_t  now, valid;

        now = ngx_time();

        valid = r->cache->valid_sec;

        if (valid == 0) {

            valid = ngx_http_file_cache_valid(u->conf->cache_valid,

                                              u->headers_in.status_n);

            if (valid) {

                r->cache->valid_sec = now + valid;

            }

        }

        if (valid) {

            r->cache->date = now;

            r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);

            if (u->headers_in.status_n == NGX_HTTP_OK

                || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT)

            {

                r->cache->last_modified = u->headers_in.last_modified_time;

                if (u->headers_in.etag) {

                    r->cache->etag = u->headers_in.etag->value;

                }

            }

            ngx_http_file_cache_set_header(r, u->buffer.start);

        } else {

            u->cacheable = 0;

        }

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http cacheable: %d", u->cacheable);

    if (u->cacheable == 0 && r->cache) {

        ngx_http_file_cache_free(r->cache, u->pipe->temp_file);

    }

#endif

    p = u->pipe;

    p->output_filter = (ngx_event_pipe_output_filter_pt) ngx_http_output_filter;

    p->output_ctx = r;

    p->tag = u->output.tag;

    p->bufs = u->conf->bufs;

    p->busy_size = u->conf->busy_buffers_size;

    p->upstream = u->peer.connection;

    p->downstream = c;

    p->pool = r->pool;

    p->log = c->log;

    p->limit_rate = u->conf->limit_rate;

    p->start_sec = ngx_time();

    p->cacheable = u->cacheable || u->store;

    p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

    if (p->temp_file == NULL) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    p->temp_file->file.fd = NGX_INVALID_FILE;

    p->temp_file->file.log = c->log;

    p->temp_file->path = u->conf->temp_path;

    p->temp_file->pool = r->pool;

    if (p->cacheable) {

        p->temp_file->persistent = 1;

    } else {

        p->temp_file->log_level = NGX_LOG_WARN;

        p->temp_file->warn = "an upstream response is buffered "

                             "to a temporary file";

    }

    p->max_temp_file_size = u->conf->max_temp_file_size;

    p->temp_file_write_size = u->conf->temp_file_write_size;

    p->preread_bufs = ngx_alloc_chain_link(r->pool);

    if (p->preread_bufs == NULL) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    p->preread_bufs->buf = &u->buffer;

    p->preread_bufs->next = NULL;

    u->buffer.recycled = 1;

    p->preread_size = u->buffer.last - u->buffer.pos;

    if (u->cacheable) {

        p->buf_to_file = ngx_calloc_buf(r->pool);

        if (p->buf_to_file == NULL) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

        p->buf_to_file->start = u->buffer.start;

        p->buf_to_file->pos = u->buffer.start;

        p->buf_to_file->last = u->buffer.pos;

        p->buf_to_file->temporary = 1;

    }

    if (ngx_event_flags & NGX_USE_AIO_EVENT) {

        /* the posted aio operation may corrupt a shadow buffer */

        p->single_buf = 1;

    }

    /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */

    p->free_bufs = 1;

    /*

     * event_pipe would do u->buffer.last += p->preread_size

     * as though these bytes were read

     */

    u->buffer.last = u->buffer.pos;

    if (u->conf->cyclic_temp_file) {

        /*

         * we need to disable the use of sendfile() if we use cyclic temp file

         * because the writing a new data may interfere with sendfile()

         * that uses the same kernel file pages (at least on FreeBSD)

         */

        p->cyclic_temp_file = 1;

        c->sendfile = 0;

    } else {

        p->cyclic_temp_file = 0;

    }

    p->read_timeout = u->conf->read_timeout;

    p->send_timeout = clcf->send_timeout;

    p->send_lowat = clcf->send_lowat;

    p->length = -1;

    if (u->input_filter_init

        && u->input_filter_init(p->input_ctx) != NGX_OK)

    {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    u->read_event_handler = ngx_http_upstream_process_upstream;

    r->write_event_handler = ngx_http_upstream_process_downstream;

    ngx_http_upstream_process_upstream(r, u);

}

static void

‌ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    int                        tcp_nodelay;

    ngx_connection_t          *c;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    /* TODO: prevent upgrade if not requested or not possible */

    r->keepalive = 0;

    c->log->action = "proxying upgraded connection";

    u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;

    u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;

    r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;

    r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;

    if (clcf->tcp_nodelay) {

        tcp_nodelay = 1;

        if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");

            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,

                           (const void *) &tcp_nodelay, sizeof(int)) == -1)

            {

                ngx_connection_error(c, ngx_socket_errno,

                                     "setsockopt(TCP_NODELAY) failed");

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            c->tcp_nodelay = NGX_TCP_NODELAY_SET;

        }

        if (u->peer.connection->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->peer.connection->log, 0,

                           "tcp_nodelay");

            if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,

                           (const void *) &tcp_nodelay, sizeof(int)) == -1)

            {

                ngx_connection_error(u->peer.connection, ngx_socket_errno,

                                     "setsockopt(TCP_NODELAY) failed");

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;

        }

    }

    if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (u->peer.connection->read->ready

        || u->buffer.pos != u->buffer.last)

    {

        ngx_post_event(c->read, &ngx_posted_events);

        ngx_http_upstream_process_upgraded(r, 1, 1);

        return;

    }

    ngx_http_upstream_process_upgraded(r, 0, 1);

}

static void

‌ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)

{

    ngx_http_upstream_process_upgraded(r, 0, 0);

}

static void

‌ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)

{

    ngx_http_upstream_process_upgraded(r, 1, 1);

}

static void

‌ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_http_upstream_process_upgraded(r, 1, 0);

}

static void

‌ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_http_upstream_process_upgraded(r, 0, 1);

}

static void

‌ngx_http_upstream_process_upgraded(ngx_http_request_t *r,

    ngx_uint_t from_upstream, ngx_uint_t do_write)

{

    size_t                     size;

    ssize_t                    n;

    ngx_buf_t                 *b;

    ngx_connection_t          *c, *downstream, *upstream, *dst, *src;

    ngx_http_upstream_t       *u;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    u = r->upstream;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process upgraded, fu:%ui", from_upstream);

    downstream = c;

    upstream = u->peer.connection;

    if (downstream->write->timedout) {

        c->timedout = 1;

        ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    if (upstream->read->timedout || upstream->write->timedout) {

        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);

        return;

    }

    if (from_upstream) {

        src = upstream;

        dst = downstream;

        b = &u->buffer;

    } else {

        src = downstream;

        dst = upstream;

        b = &u->from_client;

        if (r->header_in->last > r->header_in->pos) {

            b = r->header_in;

            b->end = b->last;

            do_write = 1;

        }

        if (b->start == NULL) {

            b->start = ngx_palloc(r->pool, u->conf->buffer_size);

            if (b->start == NULL) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

            b->pos = b->start;

            b->last = b->start;

            b->end = b->start + u->conf->buffer_size;

            b->temporary = 1;

            b->tag = u->output.tag;

        }

    }

    for ( ;; ) {

        if (do_write) {

            size = b->last - b->pos;

            if (size && dst->write->ready) {

                n = dst->send(dst, b->pos, size);

                if (n == NGX_ERROR) {

                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                    return;

                }

                if (n > 0) {

                    b->pos += n;

                    if (b->pos == b->last) {

                        b->pos = b->start;

                        b->last = b->start;

                    }

                }

            }

        }

        size = b->end - b->last;

        if (size && src->read->ready) {

            n = src->recv(src, b->last, size);

            if (n == NGX_AGAIN || n == 0) {

                break;

            }

            if (n > 0) {

                do_write = 1;

                b->last += n;

                continue;

            }

            if (n == NGX_ERROR) {

                src->read->eof = 1;

            }

        }

        break;

    }

    if ((upstream->read->eof && u->buffer.pos == u->buffer.last)

        || (downstream->read->eof && u->from_client.pos == u->from_client.last)

        || (downstream->read->eof && upstream->read->eof))

    {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "http upstream upgraded done");

        ngx_http_upstream_finalize_request(r, u, 0);

        return;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)

        != NGX_OK)

    {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (upstream->write->active && !upstream->write->ready) {

        ngx_add_timer(upstream->write, u->conf->send_timeout);

    } else if (upstream->write->timer_set) {

        ngx_del_timer(upstream->write);

    }

    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (upstream->read->active && !upstream->read->ready) {

        ngx_add_timer(upstream->read, u->conf->read_timeout);

    } else if (upstream->read->timer_set) {

        ngx_del_timer(upstream->read);

    }

    if (ngx_handle_write_event(downstream->write, clcf->send_lowat)

        != NGX_OK)

    {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (downstream->write->active && !downstream->write->ready) {

        ngx_add_timer(downstream->write, clcf->send_timeout);

    } else if (downstream->write->timer_set) {

        ngx_del_timer(downstream->write);

    }

}

static void

‌ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)

{

    ngx_event_t          *wev;

    ngx_connection_t     *c;

    ngx_http_upstream_t  *u;

    c = r->connection;

    u = r->upstream;

    wev = c->write;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process non buffered downstream");

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

    if (wev->timedout) {

        c->timedout = 1;

        ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);

        return;

    }

    ngx_http_upstream_process_non_buffered_request(r, 1);

}

static void

‌ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_connection_t  *c;

    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process non buffered upstream");

    c->log->action = "reading upstream";

    if (c->read->timedout) {

        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");

        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);

        return;

    }

    ngx_http_upstream_process_non_buffered_request(r, 0);

}

static void

‌ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,

    ngx_uint_t do_write)

{

    size_t                     size;

    ssize_t                    n;

    ngx_buf_t                 *b;

    ngx_int_t                  rc;

    ngx_connection_t          *downstream, *upstream;

    ngx_http_upstream_t       *u;

    ngx_http_core_loc_conf_t  *clcf;

    u = r->upstream;

    downstream = r->connection;

    upstream = u->peer.connection;

    b = &u->buffer;

    do_write = do_write || u->length == 0;

    for ( ;; ) {

        if (do_write) {

            if (u->out_bufs || u->busy_bufs) {

                rc = ngx_http_output_filter(r, u->out_bufs);

                if (rc == NGX_ERROR) {

                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                    return;

                }

                ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,

                                        &u->out_bufs, u->output.tag);

            }

            if (u->busy_bufs == NULL) {

                if (u->length == 0

                    || (upstream->read->eof && u->length == -1))

                {

                    ngx_http_upstream_finalize_request(r, u, 0);

                    return;

                }

                if (upstream->read->eof) {

                    ngx_log_error(NGX_LOG_ERR, upstream->log, 0,

                                  "upstream prematurely closed connection");

                    ngx_http_upstream_finalize_request(r, u,

                                                       NGX_HTTP_BAD_GATEWAY);

                    return;

                }

                if (upstream->read->error) {

                    ngx_http_upstream_finalize_request(r, u,

                                                       NGX_HTTP_BAD_GATEWAY);

                    return;

                }

                b->pos = b->start;

                b->last = b->start;

            }

        }

        size = b->end - b->last;

        if (size && upstream->read->ready) {

            n = upstream->recv(upstream, b->last, size);

            if (n == NGX_AGAIN) {

                break;

            }

            if (n > 0) {

                u->state->response_length += n;

                if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {

                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                    return;

                }

            }

            do_write = 1;

            continue;

        }

        break;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (downstream->data == r) {

        if (ngx_handle_write_event(downstream->write, clcf->send_lowat)

            != NGX_OK)

        {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

    }

    if (downstream->write->active && !downstream->write->ready) {

        ngx_add_timer(downstream->write, clcf->send_timeout);

    } else if (downstream->write->timer_set) {

        ngx_del_timer(downstream->write);

    }

    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {

        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        return;

    }

    if (upstream->read->active && !upstream->read->ready) {

        ngx_add_timer(upstream->read, u->conf->read_timeout);

    } else if (upstream->read->timer_set) {

        ngx_del_timer(upstream->read);

    }

}

static ngx_int_t

‌ngx_http_upstream_non_buffered_filter_init(void *data)

{

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)

{

    ngx_http_request_t  *r = data;

    ngx_buf_t            *b;

    ngx_chain_t          *cl, **ll;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {

        ll = &cl->next;

    }

    cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    *ll = cl;

    cl->buf->flush = 1;

    cl->buf->memory = 1;

    b = &u->buffer;

    cl->buf->pos = b->last;

    b->last += bytes;

    cl->buf->last = b->last;

    cl->buf->tag = u->output.tag;

    if (u->length == -1) {

        return NGX_OK;

    }

    u->length -= bytes;

    return NGX_OK;

}

static void

‌ngx_http_upstream_process_downstream(ngx_http_request_t *r)

{

    ngx_event_t          *wev;

    ngx_connection_t     *c;

    ngx_event_pipe_t     *p;

    ngx_http_upstream_t  *u;

    c = r->connection;

    u = r->upstream;

    p = u->pipe;

    wev = c->write;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process downstream");

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

    if (wev->timedout) {

        if (wev->delayed) {

            wev->timedout = 0;

            wev->delayed = 0;

            if (!wev->ready) {

                ngx_add_timer(wev, p->send_timeout);

                if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {

                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                }

                return;

            }

            if (ngx_event_pipe(p, wev->write) == NGX_ABORT) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

        } else {

            p->downstream_error = 1;

            c->timedout = 1;

            ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");

        }

    } else {

        if (wev->delayed) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http downstream delayed");

            if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            }

            return;

        }

        if (ngx_event_pipe(p, 1) == NGX_ABORT) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

    }

    ngx_http_upstream_process_request(r);

}

static void

‌ngx_http_upstream_process_upstream(ngx_http_request_t *r,

    ngx_http_upstream_t *u)

{

    ngx_event_t       *rev;

    ngx_event_pipe_t  *p;

    ngx_connection_t  *c;

    c = u->peer.connection;

    p = u->pipe;

    rev = c->read;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http upstream process upstream");

    c->log->action = "reading upstream";

    if (rev->timedout) {

        if (rev->delayed) {

            rev->timedout = 0;

            rev->delayed = 0;

            if (!rev->ready) {

                ngx_add_timer(rev, p->read_timeout);

                if (ngx_handle_read_event(rev, 0) != NGX_OK) {

                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                }

                return;

            }

            if (ngx_event_pipe(p, 0) == NGX_ABORT) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

                return;

            }

        } else {

            p->upstream_error = 1;

            ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");

        }

    } else {

        if (rev->delayed) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http upstream delayed");

            if (ngx_handle_read_event(rev, 0) != NGX_OK) {

                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            }

            return;

        }

        if (ngx_event_pipe(p, 0) == NGX_ABORT) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

            return;

        }

    }

    ngx_http_upstream_process_request(r);

}

static void

‌ngx_http_upstream_process_request(ngx_http_request_t *r)

{

    ngx_temp_file_t      *tf;

    ngx_event_pipe_t     *p;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    p = u->pipe;

    if (u->peer.connection) {

        if (u->store) {

            if (p->upstream_eof || p->upstream_done) {

                tf = p->temp_file;

                if (u->headers_in.status_n == NGX_HTTP_OK

                    && (p->upstream_done || p->length == -1)

                    && (u->headers_in.content_length_n == -1

                        || u->headers_in.content_length_n == tf->offset))

                {

                    ngx_http_upstream_store(r, u);

                }

            }

        }

#if (NGX_HTTP_CACHE)

        if (u->cacheable) {

            if (p->upstream_done) {

                ngx_http_file_cache_update(r, p->temp_file);

            } else if (p->upstream_eof) {

                tf = p->temp_file;

                if (p->length == -1

                    && (u->headers_in.content_length_n == -1

                        || u->headers_in.content_length_n

                           == tf->offset - (off_t) r->cache->body_start))

                {

                    ngx_http_file_cache_update(r, tf);

                } else {

                    ngx_http_file_cache_free(r->cache, tf);

                }

            } else if (p->upstream_error) {

                ngx_http_file_cache_free(r->cache, p->temp_file);

            }

        }

#endif

        if (p->upstream_done || p->upstream_eof || p->upstream_error) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http upstream exit: %p", p->out);

            if (p->upstream_done

                || (p->upstream_eof && p->length == -1))

            {

                ngx_http_upstream_finalize_request(r, u, 0);

                return;

            }

            if (p->upstream_eof) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream prematurely closed connection");

            }

            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);

            return;

        }

    }

    if (p->downstream_error) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http upstream downstream error");

        if (!u->cacheable && !u->store && u->peer.connection) {

            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);

        }

    }

}

static void

‌ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    size_t                  root;

    time_t                  lm;

    ngx_str_t               path;

    ngx_temp_file_t        *tf;

    ngx_ext_rename_file_t   ext;

    tf = u->pipe->temp_file;

    if (tf->file.fd == NGX_INVALID_FILE) {

        /* create file for empty 200 response */

        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

        if (tf == NULL) {

            return;

        }

        tf->file.fd = NGX_INVALID_FILE;

        tf->file.log = r->connection->log;

        tf->path = u->conf->temp_path;

        tf->pool = r->pool;

        tf->persistent = 1;

        if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,

                                 tf->persistent, tf->clean, tf->access)

            != NGX_OK)

        {

            return;

        }

        u->pipe->temp_file = tf;

    }

    ext.access = u->conf->store_access;

    ext.path_access = u->conf->store_access;

    ext.time = -1;

    ext.create_path = 1;

    ext.delete_file = 1;

    ext.log = r->connection->log;

    if (u->headers_in.last_modified) {

        lm = ngx_http_parse_time(u->headers_in.last_modified->value.data,

                                 u->headers_in.last_modified->value.len);

        if (lm != NGX_ERROR) {

            ext.time = lm;

            ext.fd = tf->file.fd;

        }

    }

    if (u->conf->store_lengths == NULL) {

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

            return;

        }

    } else {

        if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,

                                u->conf->store_values->elts)

            == NULL)

        {

            return;

        }

    }

    path.len--;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "upstream stores \"%s\" to \"%s\"",

                   tf->file.name.data, path.data);

    (void) ngx_ext_rename_file(&tf->file.name, &path, &ext);

    u->store = 0;

}

static void

‌ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http upstream dummy handler");

}

static void

‌ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,

    ngx_uint_t ft_type)

{

    ngx_msec_t  timeout;

    ngx_uint_t  status, state;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http next upstream, %xi", ft_type);

    if (u->peer.sockaddr) {

        if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403

            || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)

        {

            state = NGX_PEER_NEXT;

        } else {

            state = NGX_PEER_FAILED;

        }

        u->peer.free(&u->peer, u->peer.data, state);

        u->peer.sockaddr = NULL;

    }

    if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,

                      "upstream timed out");

    }

    if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {

        status = 0;

        /* TODO: inform balancer instead */

        u->peer.tries++;

    } else {

        switch (ft_type) {

        case NGX_HTTP_UPSTREAM_FT_TIMEOUT:

            status = NGX_HTTP_GATEWAY_TIME_OUT;

            break;

        case NGX_HTTP_UPSTREAM_FT_HTTP_500:

            status = NGX_HTTP_INTERNAL_SERVER_ERROR;

            break;

        case NGX_HTTP_UPSTREAM_FT_HTTP_403:

            status = NGX_HTTP_FORBIDDEN;

            break;

        case NGX_HTTP_UPSTREAM_FT_HTTP_404:

            status = NGX_HTTP_NOT_FOUND;

            break;

        /*

         * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING

         * never reach here

         */

        default:

            status = NGX_HTTP_BAD_GATEWAY;

        }

    }

    if (r->connection->error) {

        ngx_http_upstream_finalize_request(r, u,

                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);

        return;

    }

    if (status) {

        u->state->status = status;

        timeout = u->conf->next_upstream_timeout;

        if (u->peer.tries == 0

            || !(u->conf->next_upstream & ft_type)

            || (timeout && ngx_current_msec - u->peer.start_time >= timeout))

        {

#if (NGX_HTTP_CACHE)

            if (u->cache_status == NGX_HTTP_CACHE_EXPIRED

                && (u->conf->cache_use_stale & ft_type))

            {

                ngx_int_t  rc;

                rc = u->reinit_request(r);

                if (rc == NGX_OK) {

                    u->cache_status = NGX_HTTP_CACHE_STALE;

                    rc = ngx_http_upstream_cache_send(r, u);

                }

                ngx_http_upstream_finalize_request(r, u, rc);

                return;

            }

#endif

            ngx_http_upstream_finalize_request(r, u, status);

            return;

        }

    }

    if (u->peer.connection) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "close http upstream connection: %d",

                       u->peer.connection->fd);

#if (NGX_HTTP_SSL)

        if (u->peer.connection->ssl) {

            u->peer.connection->ssl->no_wait_shutdown = 1;

            u->peer.connection->ssl->no_send_shutdown = 1;

            (void) ngx_ssl_shutdown(u->peer.connection);

        }

#endif

        if (u->peer.connection->pool) {

            ngx_destroy_pool(u->peer.connection->pool);

        }

        ngx_close_connection(u->peer.connection);

        u->peer.connection = NULL;

    }

    ngx_http_upstream_connect(r, u);

}

static void

‌ngx_http_upstream_cleanup(void *data)

{

    ngx_http_request_t *r = data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "cleanup http upstream request: \"%V\"", &r->uri);

    ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);

}

static void

‌ngx_http_upstream_finalize_request(ngx_http_request_t *r,

    ngx_http_upstream_t *u, ngx_int_t rc)

{

    ngx_uint_t   flush;

    ngx_time_t  *tp;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http upstream request: %i", rc);

    if (u->cleanup) {

        *u->cleanup = NULL;

        u->cleanup = NULL;

    }

    if (u->resolved && u->resolved->ctx) {

        ngx_resolve_name_done(u->resolved->ctx);

        u->resolved->ctx = NULL;

    }

    if (u->state && u->state->response_sec) {

        tp = ngx_timeofday();

        u->state->response_sec = tp->sec - u->state->response_sec;

        u->state->response_msec = tp->msec - u->state->response_msec;

        if (u->pipe && u->pipe->read_length) {

            u->state->response_length = u->pipe->read_length;

        }

    }

    u->finalize_request(r, rc);

    if (u->peer.free && u->peer.sockaddr) {

        u->peer.free(&u->peer, u->peer.data, 0);

        u->peer.sockaddr = NULL;

    }

    if (u->peer.connection) {

#if (NGX_HTTP_SSL)

        /* TODO: do not shutdown persistent connection */

        if (u->peer.connection->ssl) {

            /*

             * We send the "close notify" shutdown alert to the upstream only

             * and do not wait its "close notify" shutdown alert.

             * It is acceptable according to the TLS standard.

             */

            u->peer.connection->ssl->no_wait_shutdown = 1;

            (void) ngx_ssl_shutdown(u->peer.connection);

        }

#endif

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "close http upstream connection: %d",

                       u->peer.connection->fd);

        if (u->peer.connection->pool) {

            ngx_destroy_pool(u->peer.connection->pool);

        }

        ngx_close_connection(u->peer.connection);

    }

    u->peer.connection = NULL;

    if (u->pipe && u->pipe->temp_file) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http upstream temp fd: %d",

                       u->pipe->temp_file->file.fd);

    }

    if (u->store && u->pipe && u->pipe->temp_file

        && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)

    {

        if (ngx_delete_file(u->pipe->temp_file->file.name.data)

            == NGX_FILE_ERROR)

        {

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                          ngx_delete_file_n " \"%s\" failed",

                          u->pipe->temp_file->file.name.data);

        }

    }

#if (NGX_HTTP_CACHE)

    if (r->cache) {

        if (u->cacheable) {

            if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {

                time_t  valid;

                valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);

                if (valid) {

                    r->cache->valid_sec = ngx_time() + valid;

                    r->cache->error = rc;

                }

            }

        }

        ngx_http_file_cache_free(r->cache, u->pipe->temp_file);

    }

#endif

    if (r->subrequest_in_memory

        && u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE)

    {

        u->buffer.last = u->buffer.pos;

    }

    if (rc == NGX_DECLINED) {

        return;

    }

    r->connection->log->action = "sending to client";

    if (!u->header_sent

        || rc == NGX_HTTP_REQUEST_TIME_OUT

        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)

    {

        ngx_http_finalize_request(r, rc);

        return;

    }

    flush = 0;

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        rc = NGX_ERROR;

        flush = 1;

    }

    if (r->header_only) {

        ngx_http_finalize_request(r, rc);

        return;

    }

    if (rc == 0) {

        rc = ngx_http_send_special(r, NGX_HTTP_LAST);

    } else if (flush) {

        r->keepalive = 0;

        rc = ngx_http_send_special(r, NGX_HTTP_FLUSH);

    }

    ngx_http_finalize_request(r, rc);

}

static ngx_int_t

‌ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_table_elt_t  **ph;

    ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);

    if (*ph == NULL) {

        *ph = h;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_content_length(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.content_length = h;

    u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_last_modified(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.last_modified = h;

#if (NGX_HTTP_CACHE)

    if (u->cacheable) {

        u->headers_in.last_modified_time = ngx_http_parse_time(h->value.data,

                                                               h->value.len);

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_array_t           *pa;

    ngx_table_elt_t      **ph;

    ngx_http_upstream_t   *u;

    u = r->upstream;

    pa = &u->headers_in.cookies;

    if (pa->elts == NULL) {

        if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    ph = ngx_array_push(pa);

    if (ph == NULL) {

        return NGX_ERROR;

    }

    *ph = h;

#if (NGX_HTTP_CACHE)

    if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {

        u->cacheable = 0;

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_cache_control(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_array_t          *pa;

    ngx_table_elt_t     **ph;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    pa = &u->headers_in.cache_control;

    if (pa->elts == NULL) {

       if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)

       {

           return NGX_ERROR;

       }

    }

    ph = ngx_array_push(pa);

    if (ph == NULL) {

        return NGX_ERROR;

    }

    *ph = h;

#if (NGX_HTTP_CACHE)

    {

    u_char     *p, *start, *last;

    ngx_int_t   n;

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {

        return NGX_OK;

    }

    if (r->cache == NULL) {

        return NGX_OK;

    }

    if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {

        return NGX_OK;

    }

    start = h->value.data;

    last = start + h->value.len;

    if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL

        || ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL

        || ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL)

    {

        u->cacheable = 0;

        return NGX_OK;

    }

    p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1);

    offset = 9;

    if (p == NULL) {

        p = ngx_strlcasestrn(start, last, (u_char *) "max-age=", 8 - 1);

        offset = 8;

    }

    if (p == NULL) {

        return NGX_OK;

    }

    n = 0;

    for (p += offset; p < last; p++) {

        if (*p == ',' || *p == ';' || *p == ' ') {

            break;

        }

        if (*p >= '0' && *p <= '9') {

            n = n * 10 + *p - '0';

            continue;

        }

        u->cacheable = 0;

        return NGX_OK;

    }

    if (n == 0) {

        u->cacheable = 0;

        return NGX_OK;

    }

    r->cache->valid_sec = ngx_time() + n;

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.expires = h;

#if (NGX_HTTP_CACHE)

    {

    time_t  expires;

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {

        return NGX_OK;

    }

    if (r->cache == NULL) {

        return NGX_OK;

    }

    if (r->cache->valid_sec != 0) {

        return NGX_OK;

    }

    expires = ngx_http_parse_time(h->value.data, h->value.len);

    if (expires == NGX_ERROR || expires < ngx_time()) {

        u->cacheable = 0;

        return NGX_OK;

    }

    r->cache->valid_sec = expires;

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.x_accel_expires = h;

#if (NGX_HTTP_CACHE)

    {

    u_char     *p;

    size_t      len;

    ngx_int_t   n;

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {

        return NGX_OK;

    }

    if (r->cache == NULL) {

        return NGX_OK;

    }

    len = h->value.len;

    p = h->value.data;

    if (p[0] != '@') {

        n = ngx_atoi(p, len);

        switch (n) {

        case 0:

            u->cacheable = 0;

            /* fall through */

        case NGX_ERROR:

            return NGX_OK;

        default:

            r->cache->valid_sec = ngx_time() + n;

            return NGX_OK;

        }

    }

    p++;

    len--;

    n = ngx_atoi(p, len);

    if (n != NGX_ERROR) {

        r->cache->valid_sec = n;

    }

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_int_t             n;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.x_accel_limit_rate = h;

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) {

        return NGX_OK;

    }

    n = ngx_atoi(h->value.data, h->value.len);

    if (n != NGX_ERROR) {

        r->limit_rate = (size_t) n;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    u_char                c0, c1, c2;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) {

        return NGX_OK;

    }

    if (u->conf->change_buffering) {

        if (h->value.len == 2) {

            c0 = ngx_tolower(h->value.data[0]);

            c1 = ngx_tolower(h->value.data[1]);

            if (c0 == 'n' && c1 == 'o') {

                u->buffering = 0;

            }

        } else if (h->value.len == 3) {

            c0 = ngx_tolower(h->value.data[0]);

            c1 = ngx_tolower(h->value.data[1]);

            c2 = ngx_tolower(h->value.data[2]);

            if (c0 == 'y' && c1 == 'e' && c2 == 's') {

                u->buffering = 1;

            }

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    if (r->upstream->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) {

        return NGX_OK;

    }

    r->headers_out.override_charset = &h->value;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    r->upstream->headers_in.connection = h;

    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,

                         (u_char *) "close", 5 - 1)

        != NULL)

    {

        r->upstream->headers_in.connection_close = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    r->upstream->headers_in.transfer_encoding = h;

    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,

                         (u_char *) "chunked", 7 - 1)

        != NULL)

    {

        r->upstream->headers_in.chunked = 1;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_process_vary(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_http_upstream_t  *u;

    u = r->upstream;

    u->headers_in.vary = h;

#if (NGX_HTTP_CACHE)

    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) {

        return NGX_OK;

    }

    if (r->cache == NULL) {

        return NGX_OK;

    }

    if (h->value.len > NGX_HTTP_CACHE_VARY_LEN

        || (h->value.len == 1 && h->value.data[0] == '*'))

    {

        u->cacheable = 0;

    }

    r->cache->vary = h->value;

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_table_elt_t  *ho, **ph;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    if (offset) {

        ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);

        *ph = ho;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_array_t      *pa;

    ngx_table_elt_t  *ho, **ph;

    pa = (ngx_array_t *) ((char *) &r->headers_out + offset);

    if (pa->elts == NULL) {

        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    ph = ngx_array_push(pa);

    if (ph == NULL) {

        return NGX_ERROR;

    }

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    *ph = ho;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    u_char  *p, *last;

    r->headers_out.content_type_len = h->value.len;

    r->headers_out.content_type = h->value;

    r->headers_out.content_type_lowcase = NULL;

    for (p = h->value.data; *p; p++) {

        if (*p != ';') {

            continue;

        }

        last = p;

        while (*++p == ' ') { /* void */ }

        if (*p == '\0') {

            return NGX_OK;

        }

        if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {

            continue;

        }

        p += 8;

        r->headers_out.content_type_len = last - h->value.data;

        if (*p == '"') {

            p++;

        }

        last = h->value.data + h->value.len;

        if (*(last - 1) == '"') {

            last--;

        }

        r->headers_out.charset.len = last - p;

        r->headers_out.charset.data = p;

        return NGX_OK;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    r->headers_out.last_modified = ho;

#if (NGX_HTTP_CACHE)

    if (r->upstream->cacheable) {

        r->headers_out.last_modified_time =

                                    r->upstream->headers_in.last_modified_time;

    }

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_int_t         rc;

    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    if (r->upstream->rewrite_redirect) {

        rc = r->upstream->rewrite_redirect(r, ho, 0);

        if (rc == NGX_DECLINED) {

            return NGX_OK;

        }

        if (rc == NGX_OK) {

            r->headers_out.location = ho;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "rewritten location: \"%V\"", &ho->value);

        }

        return rc;

    }

    if (ho->value.data[0] != '/') {

        r->headers_out.location = ho;

    }

    /*

     * we do not set r->headers_out.location here to avoid the handling

     * the local redirects without a host name by ngx_http_header_filter()

     */

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    u_char           *p;

    ngx_int_t         rc;

    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    if (r->upstream->rewrite_redirect) {

        p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1);

        if (p) {

            rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);

        } else {

            return NGX_OK;

        }

        if (rc == NGX_DECLINED) {

            return NGX_OK;

        }

        if (rc == NGX_OK) {

            r->headers_out.refresh = ho;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "rewritten refresh: \"%V\"", &ho->value);

        }

        return rc;

    }

    r->headers_out.refresh = ho;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,

    ngx_uint_t offset)

{

    ngx_int_t         rc;

    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    if (r->upstream->rewrite_cookie) {

        rc = r->upstream->rewrite_cookie(r, ho);

        if (rc == NGX_DECLINED) {

            return NGX_OK;

        }

#if (NGX_DEBUG)

        if (rc == NGX_OK) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "rewritten cookie: \"%V\"", &ho->value);

        }

#endif

        return rc;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_table_elt_t  *ho;

    if (r->upstream->conf->force_ranges) {

        return NGX_OK;

    }

#if (NGX_HTTP_CACHE)

    if (r->cached) {

        r->allow_ranges = 1;

        return NGX_OK;

    }

    if (r->upstream->cacheable) {

        r->allow_ranges = 1;

        r->single_range = 1;

        return NGX_OK;

    }

#endif

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    r->headers_out.accept_ranges = ho;

    return NGX_OK;

}

#if (NGX_HTTP_GZIP)

static ngx_int_t

‌ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,

    ngx_table_elt_t *h, ngx_uint_t offset)

{

    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);

    if (ho == NULL) {

        return NGX_ERROR;

    }

    *ho = *h;

    r->headers_out.content_encoding = ho;

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_upstream_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_upstream_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_addr_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                     *p;

    size_t                      len;

    ngx_uint_t                  i;

    ngx_http_upstream_state_t  *state;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    len = 0;

    state = r->upstream_states->elts;

    for (i = 0; i < r->upstream_states->nelts; i++) {

        if (state[i].peer) {

            len += state[i].peer->len + 2;

        } else {

            len += 3;

        }

    }

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->data = p;

    i = 0;

    for ( ;; ) {

        if (state[i].peer) {

            p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);

        }

        if (++i == r->upstream_states->nelts) {

            break;

        }

        if (state[i].peer) {

            *p++ = ',';

            *p++ = ' ';

        } else {

            *p++ = ' ';

            *p++ = ':';

            *p++ = ' ';

            if (++i == r->upstream_states->nelts) {

                break;

            }

            continue;

        }

    }

    v->len = p - v->data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_status_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                     *p;

    size_t                      len;

    ngx_uint_t                  i;

    ngx_http_upstream_state_t  *state;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    len = r->upstream_states->nelts * (3 + 2);

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->data = p;

    i = 0;

    state = r->upstream_states->elts;

    for ( ;; ) {

        if (state[i].status) {

            p = ngx_sprintf(p, "%ui", state[i].status);

        } else {

            *p++ = '-';

        }

        if (++i == r->upstream_states->nelts) {

            break;

        }

        if (state[i].peer) {

            *p++ = ',';

            *p++ = ' ';

        } else {

            *p++ = ' ';

            *p++ = ':';

            *p++ = ' ';

            if (++i == r->upstream_states->nelts) {

                break;

            }

            continue;

        }

    }

    v->len = p - v->data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_response_time_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                     *p;

    size_t                      len;

    ngx_uint_t                  i;

    ngx_msec_int_t              ms;

    ngx_http_upstream_state_t  *state;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->data = p;

    i = 0;

    state = r->upstream_states->elts;

    for ( ;; ) {

        if (state[i].status) {

            ms = (ngx_msec_int_t)

                     (state[i].response_sec * 1000 + state[i].response_msec);

            ms = ngx_max(ms, 0);

            p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000);

        } else {

            *p++ = '-';

        }

        if (++i == r->upstream_states->nelts) {

            break;

        }

        if (state[i].peer) {

            *p++ = ',';

            *p++ = ' ';

        } else {

            *p++ = ' ';

            *p++ = ':';

            *p++ = ' ';

            if (++i == r->upstream_states->nelts) {

                break;

            }

            continue;

        }

    }

    v->len = p - v->data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_response_length_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                     *p;

    size_t                      len;

    ngx_uint_t                  i;

    ngx_http_upstream_state_t  *state;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->data = p;

    i = 0;

    state = r->upstream_states->elts;

    for ( ;; ) {

        p = ngx_sprintf(p, "%O", state[i].response_length);

        if (++i == r->upstream_states->nelts) {

            break;

        }

        if (state[i].peer) {

            *p++ = ',';

            *p++ = ' ';

        } else {

            *p++ = ' ';

            *p++ = ':';

            *p++ = ' ';

            if (++i == r->upstream_states->nelts) {

                break;

            }

            continue;

        }

    }

    v->len = p - v->data;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_upstream_header_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->upstream == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,

                                         &r->upstream->headers_in.headers.part,

                                         sizeof("upstream_http_") - 1);

}

ngx_int_t

‌ngx_http_upstream_cookie_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_str_t  *name = (ngx_str_t *) data;

    ngx_str_t   cookie, s;

    if (r->upstream == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    s.len = name->len - (sizeof("upstream_cookie_") - 1);

    s.data = name->data + sizeof("upstream_cookie_") - 1;

    if (ngx_http_parse_set_cookie_lines(&r->upstream->headers_in.cookies,

                                        &s, &cookie)

        == NGX_DECLINED)

    {

        v->not_found = 1;

        return NGX_OK;

    }

    v->len = cookie.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = cookie.data;

    return NGX_OK;

}

#if (NGX_HTTP_CACHE)

ngx_int_t

‌ngx_http_upstream_cache_status(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_uint_t  n;

    if (r->upstream == NULL || r->upstream->cache_status == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    n = r->upstream->cache_status - 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->len = ngx_http_cache_status[n].len;

    v->data = ngx_http_cache_status[n].data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char  *p;

    if (r->upstream == NULL

        || !r->upstream->conf->cache_revalidate

        || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED

        || r->cache->last_modified == -1)

    {

        v->not_found = 1;

        return NGX_OK;

    }

    p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_http_time(p, r->cache->last_modified) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_cache_etag(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    if (r->upstream == NULL

        || !r->upstream->conf->cache_revalidate

        || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED

        || r->cache->etag.len == 0)

    {

        v->not_found = 1;

        return NGX_OK;

    }

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->len = r->cache->etag.len;

    v->data = r->cache->etag.data;

    return NGX_OK;

}

#endif

static char *

‌ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)

{

    char                          *rv;

    void                          *mconf;

    ngx_str_t                     *value;

    ngx_url_t                      u;

    ngx_uint_t                     m;

    ngx_conf_t                     pcf;

    ngx_http_module_t             *module;

    ngx_http_conf_ctx_t           *ctx, *http_ctx;

    ngx_http_upstream_srv_conf_t  *uscf;

    ngx_memzero(&u, sizeof(ngx_url_t));

    value = cf->args->elts;

    u.host = value[1];

    u.no_resolve = 1;

    u.no_port = 1;

    uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE

                                         |NGX_HTTP_UPSTREAM_WEIGHT

                                         |NGX_HTTP_UPSTREAM_MAX_FAILS

                                         |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT

                                         |NGX_HTTP_UPSTREAM_DOWN

                                         |NGX_HTTP_UPSTREAM_BACKUP);

    if (uscf == NULL) {

        return NGX_CONF_ERROR;

    }

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    http_ctx = cf->ctx;

    ctx->main_conf = http_ctx->main_conf;

    /* the upstream{}'s srv_conf */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->srv_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;

    uscf->srv_conf = ctx->srv_conf;

    /* the upstream{}'s loc_conf */

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (m = 0; ngx_modules[m]; m++) {

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[m]->ctx;

        if (module->create_srv_conf) {

            mconf = module->create_srv_conf(cf);

            if (mconf == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;

        }

        if (module->create_loc_conf) {

            mconf = module->create_loc_conf(cf);

            if (mconf == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->loc_conf[ngx_modules[m]->ctx_index] = mconf;

        }

    }

    uscf->servers = ngx_array_create(cf->pool, 4,

                                     sizeof(ngx_http_upstream_server_t));

    if (uscf->servers == NULL) {

        return NGX_CONF_ERROR;

    }

    /* parse inside upstream{} */

    pcf = *cf;

    cf->ctx = ctx;

    cf->cmd_type = NGX_HTTP_UPS_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    if (uscf->servers->nelts == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "no servers are inside upstream");

        return NGX_CONF_ERROR;

    }

    return rv;

}

static char *

‌ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_upstream_srv_conf_t  *uscf = conf;

    time_t                       fail_timeout;

    ngx_str_t                   *value, s;

    ngx_url_t                    u;

    ngx_int_t                    weight, max_fails;

    ngx_uint_t                   i;

    ngx_http_upstream_server_t  *us;

    us = ngx_array_push(uscf->servers);

    if (us == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));

    value = cf->args->elts;

    weight = 1;

    max_fails = 1;

    fail_timeout = 10;

    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {

                goto not_supported;

            }

            weight = ngx_atoi(&value[i].data[7], value[i].len - 7);

            if (weight == NGX_ERROR || weight == 0) {

                goto invalid;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {

                goto not_supported;

            }

            max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);

            if (max_fails == NGX_ERROR) {

                goto invalid;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {

                goto not_supported;

            }

            s.len = value[i].len - 13;

            s.data = &value[i].data[13];

            fail_timeout = ngx_parse_time(&s, 1);

            if (fail_timeout == (time_t) NGX_ERROR) {

                goto invalid;

            }

            continue;

        }

        if (ngx_strcmp(value[i].data, "backup") == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {

                goto not_supported;

            }

            us->backup = 1;

            continue;

        }

        if (ngx_strcmp(value[i].data, "down") == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {

                goto not_supported;

            }

            us->down = 1;

            continue;

        }

        goto invalid;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.default_port = 80;

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "%s in upstream \"%V\"", u.err, &u.url);

        }

        return NGX_CONF_ERROR;

    }

    us->name = u.url;

    us->addrs = u.addrs;

    us->naddrs = u.naddrs;

    us->weight = weight;

    us->max_fails = max_fails;

    us->fail_timeout = fail_timeout;

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid parameter \"%V\"", &value[i]);

    return NGX_CONF_ERROR;

not_supported:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "balancing method does not support parameter \"%V\"",

                       &value[i]);

    return NGX_CONF_ERROR;

}

ngx_http_upstream_srv_conf_t *

‌ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)

{

    ngx_uint_t                      i;

    ngx_http_upstream_server_t     *us;

    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;

    ngx_http_upstream_main_conf_t  *umcf;

    if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {

        if (ngx_parse_url(cf->pool, u) != NGX_OK) {

            if (u->err) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "%s in upstream \"%V\"", u->err, &u->url);

            }

            return NULL;

        }

    }

    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);

    uscfp = umcf->upstreams.elts;

    for (i = 0; i < umcf->upstreams.nelts; i++) {

        if (uscfp[i]->host.len != u->host.len

            || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)

               != 0)

        {

            continue;

        }

        if ((flags & NGX_HTTP_UPSTREAM_CREATE)

             && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))

        {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "duplicate upstream \"%V\"", &u->host);

            return NULL;

        }

        if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "upstream \"%V\" may not have port %d",

                               &u->host, u->port);

            return NULL;

        }

        if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {

            ngx_log_error(NGX_LOG_WARN, cf->log, 0,

                          "upstream \"%V\" may not have port %d in %s:%ui",

                          &u->host, uscfp[i]->port,

                          uscfp[i]->file_name, uscfp[i]->line);

            return NULL;

        }

        if (uscfp[i]->port && u->port

            && uscfp[i]->port != u->port)

        {

            continue;

        }

        if (uscfp[i]->default_port && u->default_port

            && uscfp[i]->default_port != u->default_port)

        {

            continue;

        }

        if (flags & NGX_HTTP_UPSTREAM_CREATE) {

            uscfp[i]->flags = flags;

        }

        return uscfp[i];

    }

    uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));

    if (uscf == NULL) {

        return NULL;

    }

    uscf->flags = flags;

    uscf->host = u->host;

    uscf->file_name = cf->conf_file->file.name.data;

    uscf->line = cf->conf_file->line;

    uscf->port = u->port;

    uscf->default_port = u->default_port;

    uscf->no_port = u->no_port;

    if (u->naddrs == 1) {

        uscf->servers = ngx_array_create(cf->pool, 1,

                                         sizeof(ngx_http_upstream_server_t));

        if (uscf->servers == NULL) {

            return NULL;

        }

        us = ngx_array_push(uscf->servers);

        if (us == NULL) {

            return NULL;

        }

        ngx_memzero(us, sizeof(ngx_http_upstream_server_t));

        us->addrs = u->addrs;

        us->naddrs = 1;

    }

    uscfp = ngx_array_push(&umcf->upstreams);

    if (uscfp == NULL) {

        return NULL;

    }

    *uscfp = uscf;

    return uscf;

}

char *

‌ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf)

{

    char  *p = conf;

    ngx_int_t                           rc;

    ngx_str_t                          *value;

    ngx_http_complex_value_t            cv;

    ngx_http_upstream_local_t         **plocal, *local;

    ngx_http_compile_complex_value_t    ccv;

    plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);

    if (*plocal != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        *plocal = NULL;

        return NGX_CONF_OK;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));

    if (local == NULL) {

        return NGX_CONF_ERROR;

    }

    *plocal = local;

    if (cv.lengths) {

        local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

        if (local->value == NULL) {

            return NGX_CONF_ERROR;

        }

        *local->value = cv;

        return NGX_CONF_OK;

    }

    local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));

    if (local->addr == NULL) {

        return NGX_CONF_ERROR;

    }

    rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);

    switch (rc) {

    case NGX_OK:

        local->addr->name = value[1];

        return NGX_CONF_OK;

    case NGX_DECLINED:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid address \"%V\"", &value[1]);

        /* fall through */

    default:

        return NGX_CONF_ERROR;

    }

}

static ngx_addr_t *

‌ngx_http_upstream_get_local(ngx_http_request_t *r,

    ngx_http_upstream_local_t *local)

{

    ngx_int_t    rc;

    ngx_str_t    val;

    ngx_addr_t  *addr;

    if (local == NULL) {

        return NULL;

    }

    if (local->value == NULL) {

        return local->addr;

    }

    if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {

        return NULL;

    }

    if (val.len == 0) {

        return NULL;

    }

    addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));

    if (addr == NULL) {

        return NULL;

    }

    rc = ngx_parse_addr(r->pool, addr, val.data, val.len);

    switch (rc) {

    case NGX_OK:

        addr->name = val;

        return addr;

    case NGX_DECLINED:

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "invalid local address \"%V\"", &val);

        /* fall through */

    default:

        return NULL;

    }

}

char *

‌ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf)

{

    char  *p = conf;

    ngx_str_t                   *value;

    ngx_array_t                **a;

    ngx_http_upstream_param_t   *param;

    a = (ngx_array_t **) (p + cmd->offset);

    if (*a == NULL) {

        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));

        if (*a == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    param = ngx_array_push(*a);

    if (param == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    param->key = value[1];

    param->value = value[2];

    param->skip_empty = 0;

    if (cf->args->nelts == 4) {

        if (ngx_strcmp(value[3].data, "if_not_empty") != 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[3]);

            return NGX_CONF_ERROR;

        }

        param->skip_empty = 1;

    }

    return NGX_CONF_OK;

}

ngx_int_t

‌ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,

    ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,

    ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)

{

    ngx_str_t       *h;

    ngx_uint_t       i, j;

    ngx_array_t      hide_headers;

    ngx_hash_key_t  *hk;

    if (conf->hide_headers == NGX_CONF_UNSET_PTR

        && conf->pass_headers == NGX_CONF_UNSET_PTR)

    {

        conf->hide_headers = prev->hide_headers;

        conf->pass_headers = prev->pass_headers;

        conf->hide_headers_hash = prev->hide_headers_hash;

        if (conf->hide_headers_hash.buckets

#if (NGX_HTTP_CACHE)

            && ((conf->cache == 0) == (prev->cache == 0))

#endif

           )

        {

            return NGX_OK;

        }

    } else {

        if (conf->hide_headers == NGX_CONF_UNSET_PTR) {

            conf->hide_headers = prev->hide_headers;

        }

        if (conf->pass_headers == NGX_CONF_UNSET_PTR) {

            conf->pass_headers = prev->pass_headers;

        }

    }

    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    for (h = default_hide_headers; h->len; h++) {

        hk = ngx_array_push(&hide_headers);

        if (hk == NULL) {

            return NGX_ERROR;

        }

        hk->key = *h;

        hk->key_hash = ngx_hash_key_lc(h->data, h->len);

        hk->value = (void *) 1;

    }

    if (conf->hide_headers != NGX_CONF_UNSET_PTR) {

        h = conf->hide_headers->elts;

        for (i = 0; i < conf->hide_headers->nelts; i++) {

            hk = hide_headers.elts;

            for (j = 0; j < hide_headers.nelts; j++) {

                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {

                    goto exist;

                }

            }

            hk = ngx_array_push(&hide_headers);

            if (hk == NULL) {

                return NGX_ERROR;

            }

            hk->key = h[i];

            hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);

            hk->value = (void *) 1;

        exist:

            continue;

        }

    }

    if (conf->pass_headers != NGX_CONF_UNSET_PTR) {

        h = conf->pass_headers->elts;

        hk = hide_headers.elts;

        for (i = 0; i < conf->pass_headers->nelts; i++) {

            for (j = 0; j < hide_headers.nelts; j++) {

                if (hk[j].key.data == NULL) {

                    continue;

                }

                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {

                    hk[j].key.data = NULL;

                    break;

                }

            }

        }

    }

    hash->hash = &conf->hide_headers_hash;

    hash->key = ngx_hash_key_lc;

    hash->pool = cf->pool;

    hash->temp_pool = NULL;

    return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts);

}

static void *

‌ngx_http_upstream_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t));

    if (umcf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&umcf->upstreams, cf->pool, 4,

                       sizeof(ngx_http_upstream_srv_conf_t *))

        != NGX_OK)

    {

        return NULL;

    }

    return umcf;

}

static char *

‌ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_upstream_main_conf_t  *umcf = conf;

    ngx_uint_t                      i;

    ngx_array_t                     headers_in;

    ngx_hash_key_t                 *hk;

    ngx_hash_init_t                 hash;

    ngx_http_upstream_init_pt       init;

    ngx_http_upstream_header_t     *header;

    ngx_http_upstream_srv_conf_t  **uscfp;

    uscfp = umcf->upstreams.elts;

    for (i = 0; i < umcf->upstreams.nelts; i++) {

        init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:

                                            ngx_http_upstream_init_round_robin;

        if (init(cf, uscfp[i]) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    /* upstream_headers_in_hash */

    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    for (header = ngx_http_upstream_headers_in; header->name.len; header++) {

        hk = ngx_array_push(&headers_in);

        if (hk == NULL) {

            return NGX_CONF_ERROR;

        }

        hk->key = header->name;

        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);

        hk->value = header;

    }

    hash.hash = &umcf->headers_in_hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = 512;

    hash.bucket_size = ngx_align(64, ngx_cacheline_size);

    hash.name = "upstream_headers_in_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/ngx_http_upstream.h - nginx-1.7.9

Data types defined

ngx_http_upstream_conf_t

ngx_http_upstream_handler_pt

ngx_http_upstream_header_t

ngx_http_upstream_headers_in_t

ngx_http_upstream_init_peer_pt

ngx_http_upstream_init_pt

ngx_http_upstream_local_t

ngx_http_upstream_main_conf_t

ngx_http_upstream_next_t

ngx_http_upstream_param_t

ngx_http_upstream_peer_t

ngx_http_upstream_resolved_t

ngx_http_upstream_s

ngx_http_upstream_server_t

ngx_http_upstream_srv_conf_s

ngx_http_upstream_srv_conf_t

ngx_http_upstream_state_t

Macros defined

NGX_HTTP_UPSTREAM_BACKUP

NGX_HTTP_UPSTREAM_CREATE

NGX_HTTP_UPSTREAM_DOWN

NGX_HTTP_UPSTREAM_FAIL_TIMEOUT

NGX_HTTP_UPSTREAM_FT_BUSY_LOCK

NGX_HTTP_UPSTREAM_FT_ERROR

NGX_HTTP_UPSTREAM_FT_HTTP_403

NGX_HTTP_UPSTREAM_FT_HTTP_404

NGX_HTTP_UPSTREAM_FT_HTTP_500

NGX_HTTP_UPSTREAM_FT_HTTP_502

NGX_HTTP_UPSTREAM_FT_HTTP_503

NGX_HTTP_UPSTREAM_FT_HTTP_504

NGX_HTTP_UPSTREAM_FT_INVALID_HEADER

NGX_HTTP_UPSTREAM_FT_MAX_WAITING

NGX_HTTP_UPSTREAM_FT_NOLIVE

NGX_HTTP_UPSTREAM_FT_OFF

NGX_HTTP_UPSTREAM_FT_STATUS

NGX_HTTP_UPSTREAM_FT_TIMEOUT

NGX_HTTP_UPSTREAM_FT_UPDATING

NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL

NGX_HTTP_UPSTREAM_IGN_EXPIRES

NGX_HTTP_UPSTREAM_IGN_SET_COOKIE

NGX_HTTP_UPSTREAM_IGN_VARY

NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING

NGX_HTTP_UPSTREAM_IGN_XA_CHARSET

NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES

NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE

NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT

NGX_HTTP_UPSTREAM_INVALID_HEADER

NGX_HTTP_UPSTREAM_MAX_FAILS

NGX_HTTP_UPSTREAM_WEIGHT

_NGX_HTTP_UPSTREAM_H_INCLUDED_

ngx_http_conf_upstream_srv_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_

‌#define _NGX_HTTP_UPSTREAM_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_connect.h>

#include <ngx_event_pipe.h>

#include <ngx_http.h>

‌#define NGX_HTTP_UPSTREAM_FT_ERROR           0x00000002

‌#define NGX_HTTP_UPSTREAM_FT_TIMEOUT         0x00000004

‌#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER  0x00000008

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_500        0x00000010

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_502        0x00000020

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_503        0x00000040

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_504        0x00000080

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_403        0x00000100

‌#define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000200

‌#define NGX_HTTP_UPSTREAM_FT_UPDATING        0x00000400

‌#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000800

‌#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00001000

‌#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000

‌#define NGX_HTTP_UPSTREAM_FT_OFF             0x80000000

‌#define NGX_HTTP_UPSTREAM_FT_STATUS          (NGX_HTTP_UPSTREAM_FT_HTTP_500  \

                                             |NGX_HTTP_UPSTREAM_FT_HTTP_502  \

                                             |NGX_HTTP_UPSTREAM_FT_HTTP_503  \

                                             |NGX_HTTP_UPSTREAM_FT_HTTP_504  \

                                             |NGX_HTTP_UPSTREAM_FT_HTTP_403  \

                                             |NGX_HTTP_UPSTREAM_FT_HTTP_404)

‌#define NGX_HTTP_UPSTREAM_INVALID_HEADER     40

‌#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT    0x00000002

‌#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES     0x00000004

‌#define NGX_HTTP_UPSTREAM_IGN_EXPIRES        0x00000008

‌#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL  0x00000010

‌#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE     0x00000020

‌#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE  0x00000040

‌#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING   0x00000080

‌#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET     0x00000100

‌#define NGX_HTTP_UPSTREAM_IGN_VARY           0x00000200

typedef struct {

    ngx_msec_t                       bl_time;

    ngx_uint_t                       bl_state;

    ngx_uint_t                       status;

    time_t                           response_sec;

    ngx_uint_t                       response_msec;

    off_t                            response_length;

    ngx_str_t                       *peer;

‌} ngx_http_upstream_state_t;

typedef struct {

    ngx_hash_t                       headers_in_hash;

    ngx_array_t                      upstreams;

                                             /* ngx_http_upstream_srv_conf_t */

‌} ngx_http_upstream_main_conf_t;

‌typedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;

‌typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us);

‌typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

typedef struct {

    ngx_http_upstream_init_pt        init_upstream;

    ngx_http_upstream_init_peer_pt   init;

    void                            *data;

‌} ngx_http_upstream_peer_t;

typedef struct {

    ngx_str_t                        name;

    ngx_addr_t                      *addrs;

    ngx_uint_t                       naddrs;

    ngx_uint_t                       weight;

    ngx_uint_t                       max_fails;

    time_t                           fail_timeout;

    unsigned                         down:1;

    unsigned                         backup:1;

‌} ngx_http_upstream_server_t;

‌#define NGX_HTTP_UPSTREAM_CREATE        0x0001

‌#define NGX_HTTP_UPSTREAM_WEIGHT        0x0002

‌#define NGX_HTTP_UPSTREAM_MAX_FAILS     0x0004

‌#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT  0x0008

‌#define NGX_HTTP_UPSTREAM_DOWN          0x0010

‌#define NGX_HTTP_UPSTREAM_BACKUP        0x0020

‌struct ngx_http_upstream_srv_conf_s {

    ngx_http_upstream_peer_t         peer;

    void                           **srv_conf;

    ngx_array_t                     *servers;  /* ngx_http_upstream_server_t */

    ngx_uint_t                       flags;

    ngx_str_t                        host;

    u_char                          *file_name;

    ngx_uint_t                       line;

    in_port_t                        port;

    in_port_t                        default_port;

    ngx_uint_t                       no_port;  /* unsigned no_port:1 */

};

typedef struct {

    ngx_addr_t                      *addr;

    ngx_http_complex_value_t        *value;

‌} ngx_http_upstream_local_t;

typedef struct {

    ngx_http_upstream_srv_conf_t    *upstream;

    ngx_msec_t                       connect_timeout;

    ngx_msec_t                       send_timeout;

    ngx_msec_t                       read_timeout;

    ngx_msec_t                       timeout;

    ngx_msec_t                       next_upstream_timeout;

    size_t                           send_lowat;

    size_t                           buffer_size;

    size_t                           limit_rate;

    size_t                           busy_buffers_size;

    size_t                           max_temp_file_size;

    size_t                           temp_file_write_size;

    size_t                           busy_buffers_size_conf;

    size_t                           max_temp_file_size_conf;

    size_t                           temp_file_write_size_conf;

    ngx_bufs_t                       bufs;

    ngx_uint_t                       ignore_headers;

    ngx_uint_t                       next_upstream;

    ngx_uint_t                       store_access;

    ngx_uint_t                       next_upstream_tries;

    ngx_flag_t                       buffering;

    ngx_flag_t                       pass_request_headers;

    ngx_flag_t                       pass_request_body;

    ngx_flag_t                       ignore_client_abort;

    ngx_flag_t                       intercept_errors;

    ngx_flag_t                       cyclic_temp_file;

    ngx_flag_t                       force_ranges;

    ngx_path_t                      *temp_path;

    ngx_hash_t                       hide_headers_hash;

    ngx_array_t                     *hide_headers;

    ngx_array_t                     *pass_headers;

    ngx_http_upstream_local_t       *local;

#if (NGX_HTTP_CACHE)

    ngx_shm_zone_t                  *cache_zone;

    ngx_http_complex_value_t        *cache_value;

    ngx_uint_t                       cache_min_uses;

    ngx_uint_t                       cache_use_stale;

    ngx_uint_t                       cache_methods;

    ngx_flag_t                       cache_lock;

    ngx_msec_t                       cache_lock_timeout;

    ngx_msec_t                       cache_lock_age;

    ngx_flag_t                       cache_revalidate;

    ngx_array_t                     *cache_valid;

    ngx_array_t                     *cache_bypass;

    ngx_array_t                     *no_cache;

#endif

    ngx_array_t                     *store_lengths;

    ngx_array_t                     *store_values;

#if (NGX_HTTP_CACHE)

    signed                           cache:2;

#endif

    signed                           store:2;

    unsigned                         intercept_404:1;

    unsigned                         change_buffering:1;

#if (NGX_HTTP_SSL)

    ngx_ssl_t                       *ssl;

    ngx_flag_t                       ssl_session_reuse;

    ngx_http_complex_value_t        *ssl_name;

    ngx_flag_t                       ssl_server_name;

    ngx_flag_t                       ssl_verify;

#endif

    ngx_str_t                        module;

‌} ngx_http_upstream_conf_t;

typedef struct {

    ngx_str_t                        name;

    ngx_http_header_handler_pt       handler;

    ngx_uint_t                       offset;

    ngx_http_header_handler_pt       copy_handler;

    ngx_uint_t                       conf;

    ngx_uint_t                       redirect;  /* unsigned   redirect:1; */

‌} ngx_http_upstream_header_t;

typedef struct {

    ngx_list_t                       headers;

    ngx_uint_t                       status_n;

    ngx_str_t                        status_line;

    ngx_table_elt_t                 *status;

    ngx_table_elt_t                 *date;

    ngx_table_elt_t                 *server;

    ngx_table_elt_t                 *connection;

    ngx_table_elt_t                 *expires;

    ngx_table_elt_t                 *etag;

    ngx_table_elt_t                 *x_accel_expires;

    ngx_table_elt_t                 *x_accel_redirect;

    ngx_table_elt_t                 *x_accel_limit_rate;

    ngx_table_elt_t                 *content_type;

    ngx_table_elt_t                 *content_length;

    ngx_table_elt_t                 *last_modified;

    ngx_table_elt_t                 *location;

    ngx_table_elt_t                 *accept_ranges;

    ngx_table_elt_t                 *www_authenticate;

    ngx_table_elt_t                 *transfer_encoding;

    ngx_table_elt_t                 *vary;

#if (NGX_HTTP_GZIP)

    ngx_table_elt_t                 *content_encoding;

#endif

    ngx_array_t                      cache_control;

    ngx_array_t                      cookies;

    off_t                            content_length_n;

    time_t                           last_modified_time;

    unsigned                         connection_close:1;

    unsigned                         chunked:1;

‌} ngx_http_upstream_headers_in_t;

typedef struct {

    ngx_str_t                        host;

    in_port_t                        port;

    ngx_uint_t                       no_port; /* unsigned no_port:1 */

    ngx_uint_t                       naddrs;

    ngx_addr_t                      *addrs;

    struct sockaddr                 *sockaddr;

    socklen_t                        socklen;

    ngx_resolver_ctx_t              *ctx;

‌} ngx_http_upstream_resolved_t;

‌typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,

    ngx_http_upstream_t *u);

‌struct ngx_http_upstream_s {

    ngx_http_upstream_handler_pt     read_event_handler;

    ngx_http_upstream_handler_pt     write_event_handler;

    ngx_peer_connection_t            peer;

    ngx_event_pipe_t                *pipe;

    ngx_chain_t                     *request_bufs;

    ngx_output_chain_ctx_t           output;

    ngx_chain_writer_ctx_t           writer;

    ngx_http_upstream_conf_t        *conf;

#if (NGX_HTTP_CACHE)

    ngx_array_t                     *caches;

#endif

    ngx_http_upstream_headers_in_t   headers_in;

    ngx_http_upstream_resolved_t    *resolved;

    ngx_buf_t                        from_client;

    ngx_buf_t                        buffer;

    off_t                            length;

    ngx_chain_t                     *out_bufs;

    ngx_chain_t                     *busy_bufs;

    ngx_chain_t                     *free_bufs;

    ngx_int_t                      (*input_filter_init)(void *data);

    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);

    void                            *input_filter_ctx;

#if (NGX_HTTP_CACHE)

    ngx_int_t                      (*create_key)(ngx_http_request_t *r);

#endif

    ngx_int_t                      (*create_request)(ngx_http_request_t *r);

    ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);

    ngx_int_t                      (*process_header)(ngx_http_request_t *r);

    void                           (*abort_request)(ngx_http_request_t *r);

    void                           (*finalize_request)(ngx_http_request_t *r,

                                         ngx_int_t rc);

    ngx_int_t                      (*rewrite_redirect)(ngx_http_request_t *r,

                                         ngx_table_elt_t *h, size_t prefix);

    ngx_int_t                      (*rewrite_cookie)(ngx_http_request_t *r,

                                         ngx_table_elt_t *h);

    ngx_msec_t                       timeout;

    ngx_http_upstream_state_t       *state;

    ngx_str_t                        method;

    ngx_str_t                        schema;

    ngx_str_t                        uri;

#if (NGX_HTTP_SSL)

    ngx_str_t                        ssl_name;

#endif

    ngx_http_cleanup_pt             *cleanup;

    unsigned                         store:1;

    unsigned                         cacheable:1;

    unsigned                         accel:1;

    unsigned                         ssl:1;

#if (NGX_HTTP_CACHE)

    unsigned                         cache_status:3;

#endif

    unsigned                         buffering:1;

    unsigned                         keepalive:1;

    unsigned                         upgrade:1;

    unsigned                         request_sent:1;

    unsigned                         header_sent:1;

};

typedef struct {

    ngx_uint_t                      status;

    ngx_uint_t                      mask;

‌} ngx_http_upstream_next_t;

typedef struct {

    ngx_str_t   key;

    ngx_str_t   value;

    ngx_uint_t  skip_empty;

‌} ngx_http_upstream_param_t;

ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);

void ngx_http_upstream_init(ngx_http_request_t *r);

ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,

    ngx_url_t *u, ngx_uint_t flags);

char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,

    ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,

    ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);

‌#define ngx_http_conf_upstream_srv_conf(uscf, module)                         \

    uscf->srv_conf[module.ctx_index]

extern ngx_module_t        ngx_http_upstream_module;

extern ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[];

extern ngx_conf_bitmask_t  ngx_http_upstream_ignore_headers_masks[];

#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */

src/event/ngx_event_pipe.c - nginx-1.7.9

Functions defined

ngx_event_pipe

ngx_event_pipe_add_free_buf

ngx_event_pipe_copy_input_filter

ngx_event_pipe_drain_chains

ngx_event_pipe_read_upstream

ngx_event_pipe_remove_shadow_links

ngx_event_pipe_write_chain_to_temp_file

ngx_event_pipe_write_to_downstream

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_event_pipe.h>

static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p);

static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p);

static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p);

static ngx_inline void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf);

static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p);

ngx_int_t

‌ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)

{

    u_int         flags;

    ngx_int_t     rc;

    ngx_event_t  *rev, *wev;

    for ( ;; ) {

        if (do_write) {

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

            rc = ngx_event_pipe_write_to_downstream(p);

            if (rc == NGX_ABORT) {

                return NGX_ABORT;

            }

            if (rc == NGX_BUSY) {

                return NGX_OK;

            }

        }

        p->read = 0;

        p->upstream_blocked = 0;

        p->log->action = "reading upstream";

        if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {

            return NGX_ABORT;

        }

        if (!p->read && !p->upstream_blocked) {

            break;

        }

        do_write = 1;

    }

    if (p->upstream->fd != (ngx_socket_t) -1) {

        rev = p->upstream->read;

        flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;

        if (ngx_handle_read_event(rev, flags) != NGX_OK) {

            return NGX_ABORT;

        }

        if (!rev->delayed) {

            if (rev->active && !rev->ready) {

                ngx_add_timer(rev, p->read_timeout);

            } else if (rev->timer_set) {

                ngx_del_timer(rev);

            }

        }

    }

    if (p->downstream->fd != (ngx_socket_t) -1

        && p->downstream->data == p->output_ctx)

    {

        wev = p->downstream->write;

        if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {

            return NGX_ABORT;

        }

        if (!wev->delayed) {

            if (wev->active && !wev->ready) {

                ngx_add_timer(wev, p->send_timeout);

            } else if (wev->timer_set) {

                ngx_del_timer(wev);

            }

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)

{

    off_t         limit;

    ssize_t       n, size;

    ngx_int_t     rc;

    ngx_buf_t    *b;

    ngx_msec_t    delay;

    ngx_chain_t  *chain, *cl, *ln;

    if (p->upstream_eof || p->upstream_error || p->upstream_done) {

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                   "pipe read upstream: %d", p->upstream->read->ready);

    for ( ;; ) {

        if (p->upstream_eof || p->upstream_error || p->upstream_done) {

            break;

        }

        if (p->preread_bufs == NULL && !p->upstream->read->ready) {

            break;

        }

        if (p->preread_bufs) {

            /* use the pre-read bufs if they exist */

            chain = p->preread_bufs;

            p->preread_bufs = NULL;

            n = p->preread_size;

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                           "pipe preread: %z", n);

            if (n) {

                p->read = 1;

            }

        } else {

#if (NGX_HAVE_KQUEUE)

            /*

             * kqueue notifies about the end of file or a pending error.

             * This test allows not to allocate a buf on these conditions

             * and not to call c->recv_chain().

             */

            if (p->upstream->read->available == 0

                && p->upstream->read->pending_eof)

            {

                p->upstream->read->ready = 0;

                p->upstream->read->eof = 1;

                p->upstream_eof = 1;

                p->read = 1;

                if (p->upstream->read->kq_errno) {

                    p->upstream->read->error = 1;

                    p->upstream_error = 1;

                    p->upstream_eof = 0;

                    ngx_log_error(NGX_LOG_ERR, p->log,

                                  p->upstream->read->kq_errno,

                                  "kevent() reported that upstream "

                                  "closed connection");

                }

                break;

            }

#endif

            if (p->limit_rate) {

                if (p->upstream->read->delayed) {

                    break;

                }

                limit = (off_t) p->limit_rate * (ngx_time() - p->start_sec + 1)

                        - p->read_length;

                if (limit <= 0) {

                    p->upstream->read->delayed = 1;

                    delay = (ngx_msec_t) (- limit * 1000 / p->limit_rate + 1);

                    ngx_add_timer(p->upstream->read, delay);

                    break;

                }

            } else {

                limit = 0;

            }

            if (p->free_raw_bufs) {

                /* use the free bufs if they exist */

                chain = p->free_raw_bufs;

                if (p->single_buf) {

                    p->free_raw_bufs = p->free_raw_bufs->next;

                    chain->next = NULL;

                } else {

                    p->free_raw_bufs = NULL;

                }

            } else if (p->allocated < p->bufs.num) {

                /* allocate a new buf if it's still allowed */

                b = ngx_create_temp_buf(p->pool, p->bufs.size);

                if (b == NULL) {

                    return NGX_ABORT;

                }

                p->allocated++;

                chain = ngx_alloc_chain_link(p->pool);

                if (chain == NULL) {

                    return NGX_ABORT;

                }

                chain->buf = b;

                chain->next = NULL;

            } else if (!p->cacheable

                       && p->downstream->data == p->output_ctx

                       && p->downstream->write->ready

                       && !p->downstream->write->delayed)

            {

                /*

                 * if the bufs are not needed to be saved in a cache and

                 * a downstream is ready then write the bufs to a downstream

                 */

                p->upstream_blocked = 1;

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "pipe downstream ready");

                break;

            } else if (p->cacheable

                       || p->temp_file->offset < p->max_temp_file_size)

            {

                /*

                 * if it is allowed, then save some bufs from p->in

                 * to a temporary file, and add them to a p->out chain

                 */

                rc = ngx_event_pipe_write_chain_to_temp_file(p);

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "pipe temp offset: %O", p->temp_file->offset);

                if (rc == NGX_BUSY) {

                    break;

                }

                if (rc == NGX_AGAIN) {

                    if (ngx_event_flags & NGX_USE_LEVEL_EVENT

                        && p->upstream->read->active

                        && p->upstream->read->ready)

                    {

                        if (ngx_del_event(p->upstream->read, NGX_READ_EVENT, 0)

                            == NGX_ERROR)

                        {

                            return NGX_ABORT;

                        }

                    }

                }

                if (rc != NGX_OK) {

                    return rc;

                }

                chain = p->free_raw_bufs;

                if (p->single_buf) {

                    p->free_raw_bufs = p->free_raw_bufs->next;

                    chain->next = NULL;

                } else {

                    p->free_raw_bufs = NULL;

                }

            } else {

                /* there are no bufs to read in */

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "no pipe bufs to read in");

                break;

            }

            n = p->upstream->recv_chain(p->upstream, chain, limit);

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                           "pipe recv chain: %z", n);

            if (p->free_raw_bufs) {

                chain->next = p->free_raw_bufs;

            }

            p->free_raw_bufs = chain;

            if (n == NGX_ERROR) {

                p->upstream_error = 1;

                return NGX_ERROR;

            }

            if (n == NGX_AGAIN) {

                if (p->single_buf) {

                    ngx_event_pipe_remove_shadow_links(chain->buf);

                }

                break;

            }

            p->read = 1;

            if (n == 0) {

                p->upstream_eof = 1;

                break;

            }

        }

        delay = p->limit_rate ? (ngx_msec_t) n * 1000 / p->limit_rate : 0;

        p->read_length += n;

        cl = chain;

        p->free_raw_bufs = NULL;

        while (cl && n > 0) {

            ngx_event_pipe_remove_shadow_links(cl->buf);

            size = cl->buf->end - cl->buf->last;

            if (n >= size) {

                cl->buf->last = cl->buf->end;

                /* STUB */ cl->buf->num = p->num++;

                if (p->input_filter(p, cl->buf) == NGX_ERROR) {

                    return NGX_ABORT;

                }

                n -= size;

                ln = cl;

                cl = cl->next;

                ngx_free_chain(p->pool, ln);

            } else {

                cl->buf->last += n;

                n = 0;

            }

        }

        if (cl) {

            for (ln = cl; ln->next; ln = ln->next) { /* void */ }

            ln->next = p->free_raw_bufs;

            p->free_raw_bufs = cl;

        }

        if (delay > 0) {

            p->upstream->read->delayed = 1;

            ngx_add_timer(p->upstream->read, delay);

            break;

        }

    }

#if (NGX_DEBUG)

    for (cl = p->busy; cl; cl = cl->next) {

        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe buf busy s:%d t:%d f:%d "

                       "%p, pos %p, size: %z "

                       "file: %O, size: %z",

                       (cl->buf->shadow ? 1 : 0),

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

    for (cl = p->out; cl; cl = cl->next) {

        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe buf out  s:%d t:%d f:%d "

                       "%p, pos %p, size: %z "

                       "file: %O, size: %z",

                       (cl->buf->shadow ? 1 : 0),

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

    for (cl = p->in; cl; cl = cl->next) {

        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe buf in   s:%d t:%d f:%d "

                       "%p, pos %p, size: %z "

                       "file: %O, size: %z",

                       (cl->buf->shadow ? 1 : 0),

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

    for (cl = p->free_raw_bufs; cl; cl = cl->next) {

        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe buf free s:%d t:%d f:%d "

                       "%p, pos %p, size: %z "

                       "file: %O, size: %z",

                       (cl->buf->shadow ? 1 : 0),

                       cl->buf->temporary, cl->buf->in_file,

                       cl->buf->start, cl->buf->pos,

                       cl->buf->last - cl->buf->pos,

                       cl->buf->file_pos,

                       cl->buf->file_last - cl->buf->file_pos);

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                   "pipe length: %O", p->length);

#endif

    if (p->free_raw_bufs && p->length != -1) {

        cl = p->free_raw_bufs;

        if (cl->buf->last - cl->buf->pos >= p->length) {

            p->free_raw_bufs = cl->next;

            /* STUB */ cl->buf->num = p->num++;

            if (p->input_filter(p, cl->buf) == NGX_ERROR) {

                 return NGX_ABORT;

            }

            ngx_free_chain(p->pool, cl);

        }

    }

    if (p->length == 0) {

        p->upstream_done = 1;

        p->read = 1;

    }

    if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) {

        /* STUB */ p->free_raw_bufs->buf->num = p->num++;

        if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) {

            return NGX_ABORT;

        }

        p->free_raw_bufs = p->free_raw_bufs->next;

        if (p->free_bufs && p->buf_to_file == NULL) {

            for (cl = p->free_raw_bufs; cl; cl = cl->next) {

                if (cl->buf->shadow == NULL) {

                    ngx_pfree(p->pool, cl->buf->start);

                }

            }

        }

    }

    if (p->cacheable && (p->in || p->buf_to_file)) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe write chain");

        if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) {

            return NGX_ABORT;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)

{

    u_char            *prev;

    size_t             bsize;

    ngx_int_t          rc;

    ngx_uint_t         flush, flushed, prev_last_shadow;

    ngx_chain_t       *out, **ll, *cl;

    ngx_connection_t  *downstream;

    downstream = p->downstream;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                   "pipe write downstream: %d", downstream->write->ready);

    flushed = 0;

    for ( ;; ) {

        if (p->downstream_error) {

            return ngx_event_pipe_drain_chains(p);

        }

        if (p->upstream_eof || p->upstream_error || p->upstream_done) {

            /* pass the p->out and p->in chains to the output filter */

            for (cl = p->busy; cl; cl = cl->next) {

                cl->buf->recycled = 0;

            }

            if (p->out) {

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "pipe write downstream flush out");

                for (cl = p->out; cl; cl = cl->next) {

                    cl->buf->recycled = 0;

                }

                rc = p->output_filter(p->output_ctx, p->out);

                if (rc == NGX_ERROR) {

                    p->downstream_error = 1;

                    return ngx_event_pipe_drain_chains(p);

                }

                p->out = NULL;

            }

            if (p->in) {

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "pipe write downstream flush in");

                for (cl = p->in; cl; cl = cl->next) {

                    cl->buf->recycled = 0;

                }

                rc = p->output_filter(p->output_ctx, p->in);

                if (rc == NGX_ERROR) {

                    p->downstream_error = 1;

                    return ngx_event_pipe_drain_chains(p);

                }

                p->in = NULL;

            }

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,

                           "pipe write downstream done");

            /* TODO: free unused bufs */

            p->downstream_done = 1;

            break;

        }

        if (downstream->data != p->output_ctx

            || !downstream->write->ready

            || downstream->write->delayed)

        {

            break;

        }

        /* bsize is the size of the busy recycled bufs */

        prev = NULL;

        bsize = 0;

        for (cl = p->busy; cl; cl = cl->next) {

            if (cl->buf->recycled) {

                if (prev == cl->buf->start) {

                    continue;

                }

                bsize += cl->buf->end - cl->buf->start;

                prev = cl->buf->start;

            }

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe write busy: %uz", bsize);

        out = NULL;

        if (bsize >= (size_t) p->busy_size) {

            flush = 1;

            goto flush;

        }

        flush = 0;

        ll = NULL;

        prev_last_shadow = 1;

        for ( ;; ) {

            if (p->out) {

                cl = p->out;

                if (cl->buf->recycled) {

                    ngx_log_error(NGX_LOG_ALERT, p->log, 0,

                                  "recycled buffer in pipe out chain");

                }

                p->out = p->out->next;

            } else if (!p->cacheable && p->in) {

                cl = p->in;

                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,

                               "pipe write buf ls:%d %p %z",

                               cl->buf->last_shadow,

                               cl->buf->pos,

                               cl->buf->last - cl->buf->pos);

                if (cl->buf->recycled && prev_last_shadow) {

                    if (bsize + cl->buf->end - cl->buf->start > p->busy_size) {

                        flush = 1;

                        break;

                    }

                    bsize += cl->buf->end - cl->buf->start;

                }

                prev_last_shadow = cl->buf->last_shadow;

                p->in = p->in->next;

            } else {

                break;

            }

            cl->next = NULL;

            if (out) {

                *ll = cl;

            } else {

                out = cl;

            }

            ll = &cl->next;

        }

    flush:

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe write: out:%p, f:%d", out, flush);

        if (out == NULL) {

            if (!flush) {

                break;

            }

            /* a workaround for AIO */

            if (flushed++ > 10) {

                return NGX_BUSY;

            }

        }

        rc = p->output_filter(p->output_ctx, out);

        ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);

        if (rc == NGX_ERROR) {

            p->downstream_error = 1;

            return ngx_event_pipe_drain_chains(p);

        }

        for (cl = p->free; cl; cl = cl->next) {

            if (cl->buf->temp_file) {

                if (p->cacheable || !p->cyclic_temp_file) {

                    continue;

                }

                /* reset p->temp_offset if all bufs had been sent */

                if (cl->buf->file_last == p->temp_file->offset) {

                    p->temp_file->offset = 0;

                }

            }

            /* TODO: free buf if p->free_bufs && upstream done */

            /* add the free shadow raw buf to p->free_raw_bufs */

            if (cl->buf->last_shadow) {

                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {

                    return NGX_ABORT;

                }

                cl->buf->last_shadow = 0;

            }

            cl->buf->shadow = NULL;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)

{

    ssize_t       size, bsize, n;

    ngx_buf_t    *b;

    ngx_uint_t    prev_last_shadow;

    ngx_chain_t  *cl, *tl, *next, *out, **ll, **last_out, **last_free, fl;

    if (p->buf_to_file) {

        fl.buf = p->buf_to_file;

        fl.next = p->in;

        out = &fl;

    } else {

        out = p->in;

    }

    if (!p->cacheable) {

        size = 0;

        cl = out;

        ll = NULL;

        prev_last_shadow = 1;

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "pipe offset: %O", p->temp_file->offset);

        do {

            bsize = cl->buf->last - cl->buf->pos;

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0,

                           "pipe buf ls:%d %p, pos %p, size: %z",

                           cl->buf->last_shadow, cl->buf->start,

                           cl->buf->pos, bsize);

            if (prev_last_shadow

                && ((size + bsize > p->temp_file_write_size)

                    || (p->temp_file->offset + size + bsize

                        > p->max_temp_file_size)))

            {

                break;

            }

            prev_last_shadow = cl->buf->last_shadow;

            size += bsize;

            ll = &cl->next;

            cl = cl->next;

        } while (cl);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);

        if (ll == NULL) {

            return NGX_BUSY;

        }

        if (cl) {

           p->in = cl;

           *ll = NULL;

        } else {

           p->in = NULL;

           p->last_in = &p->in;

        }

    } else {

        p->in = NULL;

        p->last_in = &p->in;

    }

    n = ngx_write_chain_to_temp_file(p->temp_file, out);

    if (n == NGX_ERROR) {

        return NGX_ABORT;

    }

    if (p->buf_to_file) {

        p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;

        n -= p->buf_to_file->last - p->buf_to_file->pos;

        p->buf_to_file = NULL;

        out = out->next;

    }

    if (n > 0) {

        /* update previous buffer or add new buffer */

        if (p->out) {

            for (cl = p->out; cl->next; cl = cl->next) { /* void */ }

            b = cl->buf;

            if (b->file_last == p->temp_file->offset) {

                p->temp_file->offset += n;

                b->file_last = p->temp_file->offset;

                goto free;

            }

            last_out = &cl->next;

        } else {

            last_out = &p->out;

        }

        cl = ngx_chain_get_free_buf(p->pool, &p->free);

        if (cl == NULL) {

            return NGX_ABORT;

        }

        b = cl->buf;

        ngx_memzero(b, sizeof(ngx_buf_t));

        b->tag = p->tag;

        b->file = &p->temp_file->file;

        b->file_pos = p->temp_file->offset;

        p->temp_file->offset += n;

        b->file_last = p->temp_file->offset;

        b->in_file = 1;

        b->temp_file = 1;

        *last_out = cl;

    }

free:

    for (last_free = &p->free_raw_bufs;

         *last_free != NULL;

         last_free = &(*last_free)->next)

    {

        /* void */

    }

    for (cl = out; cl; cl = next) {

        next = cl->next;

        cl->next = p->free;

        p->free = cl;

        b = cl->buf;

        if (b->last_shadow) {

            tl = ngx_alloc_chain_link(p->pool);

            if (tl == NULL) {

                return NGX_ABORT;

            }

            tl->buf = b->shadow;

            tl->next = NULL;

            *last_free = tl;

            last_free = &tl->next;

            b->shadow->pos = b->shadow->start;

            b->shadow->last = b->shadow->start;

            ngx_event_pipe_remove_shadow_links(b->shadow);

        }

    }

    return NGX_OK;

}

/* the copy input filter */

ngx_int_t

‌ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)

{

    ngx_buf_t    *b;

    ngx_chain_t  *cl;

    if (buf->pos == buf->last) {

        return NGX_OK;

    }

    cl = ngx_chain_get_free_buf(p->pool, &p->free);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    b = cl->buf;

    ngx_memcpy(b, buf, sizeof(ngx_buf_t));

    b->shadow = buf;

    b->tag = p->tag;

    b->last_shadow = 1;

    b->recycled = 1;

    buf->shadow = b;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);

    if (p->in) {

        *p->last_in = cl;

    } else {

        p->in = cl;

    }

    p->last_in = &cl->next;

    if (p->length == -1) {

        return NGX_OK;

    }

    p->length -= b->last - b->pos;

    return NGX_OK;

}

static ngx_inline void

‌ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf)

{

    ngx_buf_t  *b, *next;

    b = buf->shadow;

    if (b == NULL) {

        return;

    }

    while (!b->last_shadow) {

        next = b->shadow;

        b->temporary = 0;

        b->recycled = 0;

        b->shadow = NULL;

        b = next;

    }

    b->temporary = 0;

    b->recycled = 0;

    b->last_shadow = 0;

    b->shadow = NULL;

    buf->shadow = NULL;

}

ngx_int_t

‌ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b)

{

    ngx_chain_t  *cl;

    cl = ngx_alloc_chain_link(p->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    if (p->buf_to_file && b->start == p->buf_to_file->start) {

        b->pos = p->buf_to_file->last;

        b->last = p->buf_to_file->last;

    } else {

        b->pos = b->start;

        b->last = b->start;

    }

    b->shadow = NULL;

    cl->buf = b;

    if (p->free_raw_bufs == NULL) {

        p->free_raw_bufs = cl;

        cl->next = NULL;

        return NGX_OK;

    }

    if (p->free_raw_bufs->buf->pos == p->free_raw_bufs->buf->last) {

        /* add the free buf to the list start */

        cl->next = p->free_raw_bufs;

        p->free_raw_bufs = cl;

        return NGX_OK;

    }

    /* the first free buf is partially filled, thus add the free buf after it */

    cl->next = p->free_raw_bufs->next;

    p->free_raw_bufs->next = cl;

    return NGX_OK;

}

static ngx_int_t

‌ngx_event_pipe_drain_chains(ngx_event_pipe_t *p)

{

    ngx_chain_t  *cl, *tl;

    for ( ;; ) {

        if (p->busy) {

            cl = p->busy;

            p->busy = NULL;

        } else if (p->out) {

            cl = p->out;

            p->out = NULL;

        } else if (p->in) {

            cl = p->in;

            p->in = NULL;

        } else {

            return NGX_OK;

        }

        while (cl) {

            if (cl->buf->last_shadow) {

                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {

                    return NGX_ABORT;

                }

                cl->buf->last_shadow = 0;

            }

            cl->buf->shadow = NULL;

            tl = cl->next;

            cl->next = p->free;

            p->free = cl;

            cl = tl;

        }

    }

}

src/event/ngx_event_pipe.h - nginx-1.7.9

Data types defined

ngx_event_pipe_input_filter_pt

ngx_event_pipe_output_filter_pt

ngx_event_pipe_s

ngx_event_pipe_t

Macros defined

_NGX_EVENT_PIPE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_PIPE_H_INCLUDED_

‌#define _NGX_EVENT_PIPE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌typedef struct ngx_event_pipe_s  ngx_event_pipe_t;

‌typedef ngx_int_t (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p,

                                                    ngx_buf_t *buf);

‌typedef ngx_int_t (*ngx_event_pipe_output_filter_pt)(void *data,

                                                     ngx_chain_t *chain);

‌struct ngx_event_pipe_s {

    ngx_connection_t  *upstream;

    ngx_connection_t  *downstream;

    ngx_chain_t       *free_raw_bufs;

    ngx_chain_t       *in;

    ngx_chain_t      **last_in;

    ngx_chain_t       *out;

    ngx_chain_t       *free;

    ngx_chain_t       *busy;

    /*

     * the input filter i.e. that moves HTTP/1.1 chunks

     * from the raw bufs to an incoming chain

     */

    ngx_event_pipe_input_filter_pt    input_filter;

    void                             *input_ctx;

    ngx_event_pipe_output_filter_pt   output_filter;

    void                             *output_ctx;

    unsigned           read:1;

    unsigned           cacheable:1;

    unsigned           single_buf:1;

    unsigned           free_bufs:1;

    unsigned           upstream_done:1;

    unsigned           upstream_error:1;

    unsigned           upstream_eof:1;

    unsigned           upstream_blocked:1;

    unsigned           downstream_done:1;

    unsigned           downstream_error:1;

    unsigned           cyclic_temp_file:1;

    ngx_int_t          allocated;

    ngx_bufs_t         bufs;

    ngx_buf_tag_t      tag;

    ssize_t            busy_size;

    off_t              read_length;

    off_t              length;

    off_t              max_temp_file_size;

    ssize_t            temp_file_write_size;

    ngx_msec_t         read_timeout;

    ngx_msec_t         send_timeout;

    ssize_t            send_lowat;

    ngx_pool_t        *pool;

    ngx_log_t         *log;

    ngx_chain_t       *preread_bufs;

    size_t             preread_size;

    ngx_buf_t         *buf_to_file;

    size_t             limit_rate;

    time_t             start_sec;

    ngx_temp_file_t   *temp_file;

    /* STUB */ int     num;

};

ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write);

ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);

ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b);

#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */

src/http/ngx_http_upstream_round_robin.c - nginx-1.7.9

Functions defined

ngx_http_upstream_create_round_robin_peer

ngx_http_upstream_empty_save_session

ngx_http_upstream_empty_set_session

ngx_http_upstream_free_round_robin_peer

ngx_http_upstream_get_peer

ngx_http_upstream_get_round_robin_peer

ngx_http_upstream_init_round_robin

ngx_http_upstream_init_round_robin_peer

ngx_http_upstream_save_round_robin_peer_session

ngx_http_upstream_set_round_robin_peer_session

Macros defined

ngx_http_upstream_tries

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define ngx_http_upstream_tries(p) ((p)->number                               \

                                    + ((p)->next ? (p)->next->number : 0))

static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(

    ngx_http_upstream_rr_peer_data_t *rrp);

#if (NGX_HTTP_SSL)

static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,

    void *data);

static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,

    void *data);

#endif

ngx_int_t

‌ngx_http_upstream_init_round_robin(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_url_t                      u;

    ngx_uint_t                     i, j, n, w;

    ngx_http_upstream_server_t    *server;

    ngx_http_upstream_rr_peer_t   *peer;

    ngx_http_upstream_rr_peers_t  *peers, *backup;

    us->peer.init = ngx_http_upstream_init_round_robin_peer;

    if (us->servers) {

        server = us->servers->elts;

        n = 0;

        w = 0;

        for (i = 0; i < us->servers->nelts; i++) {

            if (server[i].backup) {

                continue;

            }

            n += server[i].naddrs;

            w += server[i].naddrs * server[i].weight;

        }

        if (n == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no servers in upstream \"%V\" in %s:%ui",

                          &us->host, us->file_name, us->line);

            return NGX_ERROR;

        }

        peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)

                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));

        if (peers == NULL) {

            return NGX_ERROR;

        }

        peers->single = (n == 1);

        peers->number = n;

        peers->weighted = (w != n);

        peers->total_weight = w;

        peers->name = &us->host;

        n = 0;

        peer = peers->peer;

        for (i = 0; i < us->servers->nelts; i++) {

            if (server[i].backup) {

                continue;

            }

            for (j = 0; j < server[i].naddrs; j++) {

                peer[n].sockaddr = server[i].addrs[j].sockaddr;

                peer[n].socklen = server[i].addrs[j].socklen;

                peer[n].name = server[i].addrs[j].name;

                peer[n].weight = server[i].weight;

                peer[n].effective_weight = server[i].weight;

                peer[n].current_weight = 0;

                peer[n].max_fails = server[i].max_fails;

                peer[n].fail_timeout = server[i].fail_timeout;

                peer[n].down = server[i].down;

                peer[n].server = server[i].name;

                n++;

            }

        }

        us->peer.data = peers;

        /* backup servers */

        n = 0;

        w = 0;

        for (i = 0; i < us->servers->nelts; i++) {

            if (!server[i].backup) {

                continue;

            }

            n += server[i].naddrs;

            w += server[i].naddrs * server[i].weight;

        }

        if (n == 0) {

            return NGX_OK;

        }

        backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)

                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));

        if (backup == NULL) {

            return NGX_ERROR;

        }

        peers->single = 0;

        backup->single = 0;

        backup->number = n;

        backup->weighted = (w != n);

        backup->total_weight = w;

        backup->name = &us->host;

        n = 0;

        peer = backup->peer;

        for (i = 0; i < us->servers->nelts; i++) {

            if (!server[i].backup) {

                continue;

            }

            for (j = 0; j < server[i].naddrs; j++) {

                peer[n].sockaddr = server[i].addrs[j].sockaddr;

                peer[n].socklen = server[i].addrs[j].socklen;

                peer[n].name = server[i].addrs[j].name;

                peer[n].weight = server[i].weight;

                peer[n].effective_weight = server[i].weight;

                peer[n].current_weight = 0;

                peer[n].max_fails = server[i].max_fails;

                peer[n].fail_timeout = server[i].fail_timeout;

                peer[n].down = server[i].down;

                peer[n].server = server[i].name;

                n++;

            }

        }

        peers->next = backup;

        return NGX_OK;

    }

    /* an upstream implicitly defined by proxy_pass, etc. */

    if (us->port == 0) {

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "no port in upstream \"%V\" in %s:%ui",

                      &us->host, us->file_name, us->line);

        return NGX_ERROR;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.host = us->host;

    u.port = us->port;

    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {

        if (u.err) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "%s in upstream \"%V\" in %s:%ui",

                          u.err, &us->host, us->file_name, us->line);

        }

        return NGX_ERROR;

    }

    n = u.naddrs;

    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)

                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));

    if (peers == NULL) {

        return NGX_ERROR;

    }

    peers->single = (n == 1);

    peers->number = n;

    peers->weighted = 0;

    peers->total_weight = n;

    peers->name = &us->host;

    peer = peers->peer;

    for (i = 0; i < u.naddrs; i++) {

        peer[i].sockaddr = u.addrs[i].sockaddr;

        peer[i].socklen = u.addrs[i].socklen;

        peer[i].name = u.addrs[i].name;

        peer[i].weight = 1;

        peer[i].effective_weight = 1;

        peer[i].current_weight = 0;

        peer[i].max_fails = 1;

        peer[i].fail_timeout = 10;

    }

    us->peer.data = peers;

    /* implicitly defined upstream has no backup servers */

    return NGX_OK;

}

ngx_int_t

‌ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_uint_t                         n;

    ngx_http_upstream_rr_peer_data_t  *rrp;

    rrp = r->upstream->peer.data;

    if (rrp == NULL) {

        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));

        if (rrp == NULL) {

            return NGX_ERROR;

        }

        r->upstream->peer.data = rrp;

    }

    rrp->peers = us->peer.data;

    rrp->current = 0;

    n = rrp->peers->number;

    if (rrp->peers->next && rrp->peers->next->number > n) {

        n = rrp->peers->next->number;

    }

    if (n <= 8 * sizeof(uintptr_t)) {

        rrp->tried = &rrp->data;

        rrp->data = 0;

    } else {

        n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));

        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));

        if (rrp->tried == NULL) {

            return NGX_ERROR;

        }

    }

    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;

    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;

    r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);

#if (NGX_HTTP_SSL)

    r->upstream->peer.set_session =

                               ngx_http_upstream_set_round_robin_peer_session;

    r->upstream->peer.save_session =

                               ngx_http_upstream_save_round_robin_peer_session;

#endif

    return NGX_OK;

}

ngx_int_t

‌ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,

    ngx_http_upstream_resolved_t *ur)

{

    u_char                            *p;

    size_t                             len;

    socklen_t                          socklen;

    ngx_uint_t                         i, n;

    struct sockaddr                   *sockaddr;

    ngx_http_upstream_rr_peer_t       *peer;

    ngx_http_upstream_rr_peers_t      *peers;

    ngx_http_upstream_rr_peer_data_t  *rrp;

    rrp = r->upstream->peer.data;

    if (rrp == NULL) {

        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));

        if (rrp == NULL) {

            return NGX_ERROR;

        }

        r->upstream->peer.data = rrp;

    }

    peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)

                     + sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1));

    if (peers == NULL) {

        return NGX_ERROR;

    }

    peers->single = (ur->naddrs == 1);

    peers->number = ur->naddrs;

    peers->name = &ur->host;

    peer = peers->peer;

    if (ur->sockaddr) {

        peer[0].sockaddr = ur->sockaddr;

        peer[0].socklen = ur->socklen;

        peer[0].name = ur->host;

        peer[0].weight = 1;

        peer[0].effective_weight = 1;

        peer[0].current_weight = 0;

        peer[0].max_fails = 1;

        peer[0].fail_timeout = 10;

    } else {

        for (i = 0; i < ur->naddrs; i++) {

            socklen = ur->addrs[i].socklen;

            sockaddr = ngx_palloc(r->pool, socklen);

            if (sockaddr == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);

            switch (sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

            case AF_INET6:

                ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);

                break;

#endif

            default: /* AF_INET */

                ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);

            }

            p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);

            if (p == NULL) {

                return NGX_ERROR;

            }

            len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);

            peer[i].sockaddr = sockaddr;

            peer[i].socklen = socklen;

            peer[i].name.len = len;

            peer[i].name.data = p;

            peer[i].weight = 1;

            peer[i].effective_weight = 1;

            peer[i].current_weight = 0;

            peer[i].max_fails = 1;

            peer[i].fail_timeout = 10;

        }

    }

    rrp->peers = peers;

    rrp->current = 0;

    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {

        rrp->tried = &rrp->data;

        rrp->data = 0;

    } else {

        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))

                / (8 * sizeof(uintptr_t));

        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));

        if (rrp->tried == NULL) {

            return NGX_ERROR;

        }

    }

    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;

    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;

    r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);

#if (NGX_HTTP_SSL)

    r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;

    r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;

#endif

    return NGX_OK;

}

ngx_int_t

‌ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    ngx_int_t                      rc;

    ngx_uint_t                     i, n;

    ngx_http_upstream_rr_peer_t   *peer;

    ngx_http_upstream_rr_peers_t  *peers;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get rr peer, try: %ui", pc->tries);

    pc->cached = 0;

    pc->connection = NULL;

    peers = rrp->peers;

    /* ngx_lock_mutex(peers->mutex); */

    if (peers->single) {

        peer = &peers->peer[0];

        if (peer->down) {

            goto failed;

        }

    } else {

        /* there are several peers */

        peer = ngx_http_upstream_get_peer(rrp);

        if (peer == NULL) {

            goto failed;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get rr peer, current: %ui %i",

                       rrp->current, peer->current_weight);

    }

    pc->sockaddr = peer->sockaddr;

    pc->socklen = peer->socklen;

    pc->name = &peer->name;

    /* ngx_unlock_mutex(peers->mutex); */

    return NGX_OK;

failed:

    if (peers->next) {

        /* ngx_unlock_mutex(peers->mutex); */

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");

        rrp->peers = peers->next;

        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))

                / (8 * sizeof(uintptr_t));

        for (i = 0; i < n; i++) {

             rrp->tried[i] = 0;

        }

        rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);

        if (rc != NGX_BUSY) {

            return rc;

        }

        /* ngx_lock_mutex(peers->mutex); */

    }

    /* all peers failed, mark them as live for quick recovery */

    for (i = 0; i < peers->number; i++) {

        peers->peer[i].fails = 0;

    }

    /* ngx_unlock_mutex(peers->mutex); */

    pc->name = peers->name;

    return NGX_BUSY;

}

static ngx_http_upstream_rr_peer_t *

‌ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)

{

    time_t                        now;

    uintptr_t                     m;

    ngx_int_t                     total;

    ngx_uint_t                    i, n;

    ngx_http_upstream_rr_peer_t  *peer, *best;

    now = ngx_time();

    best = NULL;

    total = 0;

    for (i = 0; i < rrp->peers->number; i++) {

        n = i / (8 * sizeof(uintptr_t));

        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

        if (rrp->tried[n] & m) {

            continue;

        }

        peer = &rrp->peers->peer[i];

        if (peer->down) {

            continue;

        }

        if (peer->max_fails

            && peer->fails >= peer->max_fails

            && now - peer->checked <= peer->fail_timeout)

        {

            continue;

        }

        peer->current_weight += peer->effective_weight;

        total += peer->effective_weight;

        if (peer->effective_weight < peer->weight) {

            peer->effective_weight++;

        }

        if (best == NULL || peer->current_weight > best->current_weight) {

            best = peer;

        }

    }

    if (best == NULL) {

        return NULL;

    }

    i = best - &rrp->peers->peer[0];

    rrp->current = i;

    n = i / (8 * sizeof(uintptr_t));

    m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

    rrp->tried[n] |= m;

    best->current_weight -= total;

    if (now - best->checked > best->fail_timeout) {

        best->checked = now;

    }

    return best;

}

void

‌ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,

    ngx_uint_t state)

{

    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    time_t                       now;

    ngx_http_upstream_rr_peer_t  *peer;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "free rr peer %ui %ui", pc->tries, state);

    /* TODO: NGX_PEER_KEEPALIVE */

    if (rrp->peers->single) {

        pc->tries = 0;

        return;

    }

    peer = &rrp->peers->peer[rrp->current];

    if (state & NGX_PEER_FAILED) {

        now = ngx_time();

        /* ngx_lock_mutex(rrp->peers->mutex); */

        peer->fails++;

        peer->accessed = now;

        peer->checked = now;

        if (peer->max_fails) {

            peer->effective_weight -= peer->weight / peer->max_fails;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "free rr peer failed: %ui %i",

                       rrp->current, peer->effective_weight);

        if (peer->effective_weight < 0) {

            peer->effective_weight = 0;

        }

        /* ngx_unlock_mutex(rrp->peers->mutex); */

    } else {

        /* mark peer live if check passed */

        if (peer->accessed < peer->checked) {

            peer->fails = 0;

        }

    }

    if (pc->tries) {

        pc->tries--;

    }

    /* ngx_unlock_mutex(rrp->peers->mutex); */

}

#if (NGX_HTTP_SSL)

ngx_int_t

‌ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,

    void *data)

{

    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    ngx_int_t                     rc;

    ngx_ssl_session_t            *ssl_session;

    ngx_http_upstream_rr_peer_t  *peer;

    peer = &rrp->peers->peer[rrp->current];

    /* TODO: threads only mutex */

    /* ngx_lock_mutex(rrp->peers->mutex); */

    ssl_session = peer->ssl_session;

    rc = ngx_ssl_set_session(pc->connection, ssl_session);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "set session: %p", ssl_session);

    /* ngx_unlock_mutex(rrp->peers->mutex); */

    return rc;

}

void

‌ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,

    void *data)

{

    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    ngx_ssl_session_t            *old_ssl_session, *ssl_session;

    ngx_http_upstream_rr_peer_t  *peer;

    ssl_session = ngx_ssl_get_session(pc->connection);

    if (ssl_session == NULL) {

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "save session: %p", ssl_session);

    peer = &rrp->peers->peer[rrp->current];

    /* TODO: threads only mutex */

    /* ngx_lock_mutex(rrp->peers->mutex); */

    old_ssl_session = peer->ssl_session;

    peer->ssl_session = ssl_session;

    /* ngx_unlock_mutex(rrp->peers->mutex); */

    if (old_ssl_session) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "old session: %p", old_ssl_session);

        /* TODO: may block */

        ngx_ssl_free_session(old_ssl_session);

    }

}

static ngx_int_t

‌ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)

{

    return NGX_OK;

}

static void

‌ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)

{

    return;

}

#endif

src/http/ngx_http_upstream_round_robin.h - nginx-1.7.9

Data types defined

ngx_http_upstream_rr_peer_data_t

ngx_http_upstream_rr_peer_t

ngx_http_upstream_rr_peers_s

ngx_http_upstream_rr_peers_t

Macros defined

_NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_

‌#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    struct sockaddr                *sockaddr;

    socklen_t                       socklen;

    ngx_str_t                       name;

    ngx_str_t                       server;

    ngx_int_t                       current_weight;

    ngx_int_t                       effective_weight;

    ngx_int_t                       weight;

    ngx_uint_t                      fails;

    time_t                          accessed;

    time_t                          checked;

    ngx_uint_t                      max_fails;

    time_t                          fail_timeout;

    ngx_uint_t                      down;          /* unsigned  down:1; */

#if (NGX_HTTP_SSL)

    ngx_ssl_session_t              *ssl_session;   /* local to a process */

#endif

‌} ngx_http_upstream_rr_peer_t;

‌typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;

‌struct ngx_http_upstream_rr_peers_s {

    ngx_uint_t                      number;

/* ngx_mutex_t                    *mutex; */

    ngx_uint_t                      total_weight;

    unsigned                        single:1;

    unsigned                        weighted:1;

    ngx_str_t                      *name;

    ngx_http_upstream_rr_peers_t   *next;

    ngx_http_upstream_rr_peer_t     peer[1];

};

typedef struct {

    ngx_http_upstream_rr_peers_t   *peers;

    ngx_uint_t                      current;

    uintptr_t                      *tried;

    uintptr_t                       data;

‌} ngx_http_upstream_rr_peer_data_t;

ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us);

ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,

    ngx_http_upstream_resolved_t *ur);

ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,

    void *data);

void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,

    void *data, ngx_uint_t state);

#if (NGX_HTTP_SSL)

ngx_int_t

    ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,

    void *data);

void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,

    void *data);

#endif

#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */

src/http/ngx_http_file_cache.c - nginx-1.7.9

Global variables defined

ngx_http_cache_status

ngx_http_file_cache_key

Functions defined

ngx_http_cache_aio_event_handler

ngx_http_cache_send

ngx_http_file_cache_add

ngx_http_file_cache_add_file

ngx_http_file_cache_aio_read

ngx_http_file_cache_cleanup

ngx_http_file_cache_create

ngx_http_file_cache_create_key

ngx_http_file_cache_delete

ngx_http_file_cache_delete_file

ngx_http_file_cache_exists

ngx_http_file_cache_expire

ngx_http_file_cache_forced_expire

ngx_http_file_cache_free

ngx_http_file_cache_init

ngx_http_file_cache_loader

ngx_http_file_cache_loader_sleep

ngx_http_file_cache_lock

ngx_http_file_cache_lock_wait

ngx_http_file_cache_lock_wait_handler

ngx_http_file_cache_lookup

ngx_http_file_cache_manage_file

ngx_http_file_cache_manager

ngx_http_file_cache_name

ngx_http_file_cache_new

ngx_http_file_cache_noop

ngx_http_file_cache_open

ngx_http_file_cache_rbtree_insert_value

ngx_http_file_cache_read

ngx_http_file_cache_reopen

ngx_http_file_cache_set_header

ngx_http_file_cache_set_slot

ngx_http_file_cache_update

ngx_http_file_cache_update_header

ngx_http_file_cache_valid

ngx_http_file_cache_valid_set_slot

ngx_http_file_cache_vary

ngx_http_file_cache_vary_header

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_md5.h>

static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,

    ngx_http_cache_t *c);

static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);

static void ngx_http_file_cache_lock_wait(ngx_http_request_t *r,

    ngx_http_cache_t *c);

static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,

    ngx_http_cache_t *c);

static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,

    ngx_http_cache_t *c);

#if (NGX_HAVE_FILE_AIO)

static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);

#endif

static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,

    ngx_http_cache_t *c);

static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,

    ngx_path_t *path);

static ngx_http_file_cache_node_t *

    ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);

static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,

    size_t len, u_char *hash);

static void ngx_http_file_cache_vary_header(ngx_http_request_t *r,

    ngx_md5_t *md5, ngx_str_t *name);

static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,

    ngx_http_cache_t *c);

static void ngx_http_file_cache_cleanup(void *data);

static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);

static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);

static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,

    ngx_queue_t *q, u_char *name);

static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);

static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,

    ngx_http_cache_t *c);

static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

‌ngx_str_t  ngx_http_cache_status[] = {

    ngx_string("MISS"),

    ngx_string("BYPASS"),

    ngx_string("EXPIRED"),

    ngx_string("STALE"),

    ngx_string("UPDATING"),

    ngx_string("REVALIDATED"),

    ngx_string("HIT")

};

‌static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };

static ngx_int_t

‌ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)

{

    ngx_http_file_cache_t  *ocache = data;

    size_t                  len;

    ngx_uint_t              n;

    ngx_http_file_cache_t  *cache;

    cache = shm_zone->data;

    if (ocache) {

        if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {

            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,

                          "cache \"%V\" uses the \"%V\" cache path "

                          "while previously it used the \"%V\" cache path",

                          &shm_zone->shm.name, &cache->path->name,

                          &ocache->path->name);

            return NGX_ERROR;

        }

        for (n = 0; n < 3; n++) {

            if (cache->path->level[n] != ocache->path->level[n]) {

                ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,

                              "cache \"%V\" had previously different levels",

                              &shm_zone->shm.name);

                return NGX_ERROR;

            }

        }

        cache->sh = ocache->sh;

        cache->shpool = ocache->shpool;

        cache->bsize = ocache->bsize;

        cache->max_size /= cache->bsize;

        if (!cache->sh->cold || cache->sh->loading) {

            cache->path->loader = NULL;

        }

        return NGX_OK;

    }

    cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    if (shm_zone->shm.exists) {

        cache->sh = cache->shpool->data;

        cache->bsize = ngx_fs_bsize(cache->path->name.data);

        return NGX_OK;

    }

    cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));

    if (cache->sh == NULL) {

        return NGX_ERROR;

    }

    cache->shpool->data = cache->sh;

    ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,

                    ngx_http_file_cache_rbtree_insert_value);

    ngx_queue_init(&cache->sh->queue);

    cache->sh->cold = 1;

    cache->sh->loading = 0;

    cache->sh->size = 0;

    cache->bsize = ngx_fs_bsize(cache->path->name.data);

    cache->max_size /= cache->bsize;

    len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;

    cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);

    if (cache->shpool->log_ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",

                &shm_zone->shm.name);

    cache->shpool->log_nomem = 0;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_file_cache_new(ngx_http_request_t *r)

{

    ngx_http_cache_t  *c;

    c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));

    if (c == NULL) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {

        return NGX_ERROR;

    }

    r->cache = c;

    c->file.log = r->connection->log;

    c->file.fd = NGX_INVALID_FILE;

    c->last_modified = -1;

    return NGX_OK;

}

ngx_int_t

‌ngx_http_file_cache_create(ngx_http_request_t *r)

{

    ngx_http_cache_t       *c;

    ngx_pool_cleanup_t     *cln;

    ngx_http_file_cache_t  *cache;

    c = r->cache;

    cache = c->file_cache;

    cln = ngx_pool_cleanup_add(r->pool, 0);

    if (cln == NULL) {

        return NGX_ERROR;

    }

    cln->handler = ngx_http_file_cache_cleanup;

    cln->data = c;

    if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {

        return NGX_ERROR;

    }

    if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

void

‌ngx_http_file_cache_create_key(ngx_http_request_t *r)

{

    size_t             len;

    ngx_str_t         *key;

    ngx_uint_t         i;

    ngx_md5_t          md5;

    ngx_http_cache_t  *c;

    c = r->cache;

    len = 0;

    ngx_crc32_init(c->crc32);

    ngx_md5_init(&md5);

    key = c->keys.elts;

    for (i = 0; i < c->keys.nelts; i++) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http cache key: \"%V\"", &key[i]);

        len += key[i].len;

        ngx_crc32_update(&c->crc32, key[i].data, key[i].len);

        ngx_md5_update(&md5, key[i].data, key[i].len);

    }

    c->header_start = sizeof(ngx_http_file_cache_header_t)

                      + sizeof(ngx_http_file_cache_key) + len + 1;

    ngx_crc32_final(c->crc32);

    ngx_md5_final(c->key, &md5);

    ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);

}

ngx_int_t

‌ngx_http_file_cache_open(ngx_http_request_t *r)

{

    ngx_int_t                  rc, rv;

    ngx_uint_t                 cold, test;

    ngx_http_cache_t          *c;

    ngx_pool_cleanup_t        *cln;

    ngx_open_file_info_t       of;

    ngx_http_file_cache_t     *cache;

    ngx_http_core_loc_conf_t  *clcf;

    c = r->cache;

    if (c->waiting) {

        return NGX_AGAIN;

    }

    if (c->reading) {

        return ngx_http_file_cache_read(r, c);

    }

    cache = c->file_cache;

    if (c->node == NULL) {

        cln = ngx_pool_cleanup_add(r->pool, 0);

        if (cln == NULL) {

            return NGX_ERROR;

        }

        cln->handler = ngx_http_file_cache_cleanup;

        cln->data = c;

    }

    rc = ngx_http_file_cache_exists(cache, c);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache exists: %i e:%d", rc, c->exists);

    if (rc == NGX_ERROR) {

        return rc;

    }

    if (rc == NGX_AGAIN) {

        return NGX_HTTP_CACHE_SCARCE;

    }

    cold = cache->sh->cold;

    if (rc == NGX_OK) {

        if (c->error) {

            return c->error;

        }

        c->temp_file = 1;

        test = c->exists ? 1 : 0;

        rv = NGX_DECLINED;

    } else { /* rc == NGX_DECLINED */

        if (c->min_uses > 1) {

            if (!cold) {

                return NGX_HTTP_CACHE_SCARCE;

            }

            test = 1;

            rv = NGX_HTTP_CACHE_SCARCE;

        } else {

            c->temp_file = 1;

            test = cold ? 1 : 0;

            rv = NGX_DECLINED;

        }

    }

    if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {

        return NGX_ERROR;

    }

    if (!test) {

        goto done;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.uniq = c->uniq;

    of.valid = clcf->open_file_cache_valid;

    of.min_uses = clcf->open_file_cache_min_uses;

    of.events = clcf->open_file_cache_events;

    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;

    of.read_ahead = clcf->read_ahead;

    if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)

        != NGX_OK)

    {

        switch (of.err) {

        case 0:

            return NGX_ERROR;

        case NGX_ENOENT:

        case NGX_ENOTDIR:

            goto done;

        default:

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,

                          ngx_open_file_n " \"%s\" failed", c->file.name.data);

            return NGX_ERROR;

        }

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache fd: %d", of.fd);

    c->file.fd = of.fd;

    c->file.log = r->connection->log;

    c->uniq = of.uniq;

    c->length = of.size;

    c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;

    c->buf = ngx_create_temp_buf(r->pool, c->body_start);

    if (c->buf == NULL) {

        return NGX_ERROR;

    }

    return ngx_http_file_cache_read(r, c);

done:

    if (rv == NGX_DECLINED) {

        return ngx_http_file_cache_lock(r, c);

    }

    return rv;

}

static ngx_int_t

‌ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)

{

    ngx_msec_t                 now, timer;

    ngx_http_file_cache_t     *cache;

    if (!c->lock) {

        return NGX_DECLINED;

    }

    now = ngx_current_msec;

    cache = c->file_cache;

    ngx_shmtx_lock(&cache->shpool->mutex);

    timer = c->node->lock_time - now;

    if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {

        c->node->updating = 1;

        c->node->lock_time = now + c->lock_age;

        c->updating = 1;

        c->lock_time = c->node->lock_time;

    }

    ngx_shmtx_unlock(&cache->shpool->mutex);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache lock u:%d wt:%M",

                   c->updating, c->wait_time);

    if (c->updating) {

        return NGX_DECLINED;

    }

    if (c->lock_timeout == 0) {

        return NGX_HTTP_CACHE_SCARCE;

    }

    c->waiting = 1;

    if (c->wait_time == 0) {

        c->wait_time = now + c->lock_timeout;

        c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;

        c->wait_event.data = r;

        c->wait_event.log = r->connection->log;

    }

    timer = c->wait_time - now;

    ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);

    r->main->blocked++;

    return NGX_AGAIN;

}

static void

‌ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)

{

    ngx_connection_t    *c;

    ngx_http_request_t  *r;

    r = ev->data;

    c = r->connection;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http file cache wait: \"%V?%V\"", &r->uri, &r->args);

    ngx_http_file_cache_lock_wait(r, r->cache);

    ngx_http_run_posted_requests(c);

}

static void

‌ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)

{

    ngx_uint_t              wait;

    ngx_msec_t              now, timer;

    ngx_http_file_cache_t  *cache;

    now = ngx_current_msec;

    timer = c->wait_time - now;

    if ((ngx_msec_int_t) timer <= 0) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "cache lock timeout");

        c->lock_timeout = 0;

        goto wakeup;

    }

    cache = c->file_cache;

    wait = 0;

    ngx_shmtx_lock(&cache->shpool->mutex);

    timer = c->node->lock_time - now;

    if (c->node->updating && (ngx_msec_int_t) timer > 0) {

        wait = 1;

    }

    ngx_shmtx_unlock(&cache->shpool->mutex);

    if (wait) {

        ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);

        return;

    }

wakeup:

    c->waiting = 0;

    r->main->blocked--;

    r->write_event_handler(r);

}

static ngx_int_t

‌ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)

{

    time_t                         now;

    ssize_t                        n;

    ngx_int_t                      rc;

    ngx_http_file_cache_t         *cache;

    ngx_http_file_cache_header_t  *h;

    n = ngx_http_file_cache_aio_read(r, c);

    if (n < 0) {

        return n;

    }

    if ((size_t) n < c->header_start) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      "cache file \"%s\" is too small", c->file.name.data);

        return NGX_DECLINED;

    }

    h = (ngx_http_file_cache_header_t *) c->buf->pos;

    if (h->version != NGX_HTTP_CACHE_VERSION) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "cache file \"%s\" version mismatch", c->file.name.data);

        return NGX_DECLINED;

    }

    if (h->crc32 != c->crc32) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      "cache file \"%s\" has md5 collision", c->file.name.data);

        return NGX_DECLINED;

    }

    if ((size_t) h->body_start > c->body_start) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      "cache file \"%s\" has too long header",

                      c->file.name.data);

        return NGX_DECLINED;

    }

    if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      "cache file \"%s\" has incorrect vary length",

                      c->file.name.data);

        return NGX_DECLINED;

    }

    if (h->vary_len) {

        ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant);

        if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http file cache vary mismatch");

            return ngx_http_file_cache_reopen(r, c);

        }

    }

    c->buf->last += n;

    c->valid_sec = h->valid_sec;

    c->last_modified = h->last_modified;

    c->date = h->date;

    c->valid_msec = h->valid_msec;

    c->header_start = h->header_start;

    c->body_start = h->body_start;

    c->etag.len = h->etag_len;

    c->etag.data = h->etag;

    r->cached = 1;

    cache = c->file_cache;

    if (cache->sh->cold) {

        ngx_shmtx_lock(&cache->shpool->mutex);

        if (!c->node->exists) {

            c->node->uses = 1;

            c->node->body_start = c->body_start;

            c->node->exists = 1;

            c->node->uniq = c->uniq;

            c->node->fs_size = c->fs_size;

            cache->sh->size += c->fs_size;

        }

        ngx_shmtx_unlock(&cache->shpool->mutex);

    }

    now = ngx_time();

    if (c->valid_sec < now) {

        ngx_shmtx_lock(&cache->shpool->mutex);

        if (c->node->updating) {

            rc = NGX_HTTP_CACHE_UPDATING;

        } else {

            c->node->updating = 1;

            c->updating = 1;

            c->lock_time = c->node->lock_time;

            rc = NGX_HTTP_CACHE_STALE;

        }

        ngx_shmtx_unlock(&cache->shpool->mutex);

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http file cache expired: %i %T %T",

                       rc, c->valid_sec, now);

        return rc;

    }

    return NGX_OK;

}

static ssize_t

‌ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)

{

#if (NGX_HAVE_FILE_AIO)

    ssize_t                    n;

    ngx_http_core_loc_conf_t  *clcf;

    if (!ngx_file_aio) {

        goto noaio;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->aio) {

        goto noaio;

    }

    n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);

    if (n != NGX_AGAIN) {

        c->reading = 0;

        return n;

    }

    c->reading = 1;

    c->file.aio->data = r;

    c->file.aio->handler = ngx_http_cache_aio_event_handler;

    r->main->blocked++;

    r->aio = 1;

    return NGX_AGAIN;

noaio:

#endif

    return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);

}

#if (NGX_HAVE_FILE_AIO)

static void

‌ngx_http_cache_aio_event_handler(ngx_event_t *ev)

{

    ngx_event_aio_t     *aio;

    ngx_connection_t    *c;

    ngx_http_request_t  *r;

    aio = ev->data;

    r = aio->data;

    c = r->connection;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http file cache aio: \"%V?%V\"", &r->uri, &r->args);

    r->main->blocked--;

    r->aio = 0;

    r->write_event_handler(r);

    ngx_http_run_posted_requests(c);

}

#endif

static ngx_int_t

‌ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)

{

    ngx_int_t                    rc;

    ngx_http_file_cache_node_t  *fcn;

    ngx_shmtx_lock(&cache->shpool->mutex);

    fcn = c->node;

    if (fcn == NULL) {

        fcn = ngx_http_file_cache_lookup(cache, c->key);

    }

    if (fcn) {

        ngx_queue_remove(&fcn->queue);

        if (c->node == NULL) {

            fcn->uses++;

            fcn->count++;

        }

        if (fcn->error) {

            if (fcn->valid_sec < ngx_time()) {

                goto renew;

            }

            rc = NGX_OK;

            goto done;

        }

        if (fcn->exists || fcn->uses >= c->min_uses) {

            c->exists = fcn->exists;

            if (fcn->body_start) {

                c->body_start = fcn->body_start;

            }

            rc = NGX_OK;

            goto done;

        }

        rc = NGX_AGAIN;

        goto done;

    }

    fcn = ngx_slab_calloc_locked(cache->shpool,

                                 sizeof(ngx_http_file_cache_node_t));

    if (fcn == NULL) {

        ngx_shmtx_unlock(&cache->shpool->mutex);

        (void) ngx_http_file_cache_forced_expire(cache);

        ngx_shmtx_lock(&cache->shpool->mutex);

        fcn = ngx_slab_calloc_locked(cache->shpool,

                                     sizeof(ngx_http_file_cache_node_t));

        if (fcn == NULL) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "could not allocate node%s", cache->shpool->log_ctx);

            rc = NGX_ERROR;

            goto failed;

        }

    }

    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],

               NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

    ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

    fcn->uses = 1;

    fcn->count = 1;

renew:

    rc = NGX_DECLINED;

    fcn->valid_msec = 0;

    fcn->error = 0;

    fcn->exists = 0;

    fcn->valid_sec = 0;

    fcn->uniq = 0;

    fcn->body_start = 0;

    fcn->fs_size = 0;

done:

    fcn->expire = ngx_time() + cache->inactive;

    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

    c->uniq = fcn->uniq;

    c->error = fcn->error;

    c->node = fcn;

failed:

    ngx_shmtx_unlock(&cache->shpool->mutex);

    return rc;

}

static ngx_int_t

‌ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)

{

    u_char            *p;

    ngx_http_cache_t  *c;

    c = r->cache;

    if (c->file.name.len) {

        return NGX_OK;

    }

    c->file.name.len = path->name.len + 1 + path->len

                       + 2 * NGX_HTTP_CACHE_KEY_LEN;

    c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);

    if (c->file.name.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(c->file.name.data, path->name.data, path->name.len);

    p = c->file.name.data + path->name.len + 1 + path->len;

    p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);

    *p = '\0';

    ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "cache file: \"%s\"", c->file.name.data);

    return NGX_OK;

}

static ngx_http_file_cache_node_t *

‌ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)

{

    ngx_int_t                    rc;

    ngx_rbtree_key_t             node_key;

    ngx_rbtree_node_t           *node, *sentinel;

    ngx_http_file_cache_node_t  *fcn;

    ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));

    node = cache->sh->rbtree.root;

    sentinel = cache->sh->rbtree.sentinel;

    while (node != sentinel) {

        if (node_key < node->key) {

            node = node->left;

            continue;

        }

        if (node_key > node->key) {

            node = node->right;

            continue;

        }

        /* node_key == node->key */

        fcn = (ngx_http_file_cache_node_t *) node;

        rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,

                        NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

        if (rc == 0) {

            return fcn;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    /* not found */

    return NULL;

}

static void

‌ngx_http_file_cache_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_file_cache_node_t   *cn, *cnt;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            cn = (ngx_http_file_cache_node_t *) node;

            cnt = (ngx_http_file_cache_node_t *) temp;

            p = (ngx_memcmp(cn->key, cnt->key,

                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))

                 < 0)

                    ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

static void

‌ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,

    u_char *hash)

{

    u_char     *p, *last;

    ngx_str_t   name;

    ngx_md5_t   md5;

    u_char      buf[NGX_HTTP_CACHE_VARY_LEN];

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache vary: \"%*s\"", len, vary);

    ngx_md5_init(&md5);

    ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);

    ngx_strlow(buf, vary, len);

    p = buf;

    last = buf + len;

    while (p < last) {

        while (p < last && (*p == ' ' || *p == ',')) { p++; }

        name.data = p;

        while (p < last && *p != ',' && *p != ' ') { p++; }

        name.len = p - name.data;

        if (name.len == 0) {

            break;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http file cache vary: %V", &name);

        ngx_md5_update(&md5, name.data, name.len);

        ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1);

        ngx_http_file_cache_vary_header(r, &md5, &name);

        ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);

    }

    ngx_md5_final(hash, &md5);

}

static void

‌ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,

    ngx_str_t *name)

{

    size_t            len;

    u_char           *p, *start, *last;

    ngx_uint_t        i, multiple, normalize;

    ngx_list_part_t  *part;

    ngx_table_elt_t  *header;

    multiple = 0;

    normalize = 0;

    if (name->len == sizeof("Accept-Charset") - 1

        && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",

                           sizeof("Accept-Charset") - 1) == 0)

    {

        normalize = 1;

    } else if (name->len == sizeof("Accept-Encoding") - 1

        && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",

                           sizeof("Accept-Encoding") - 1) == 0)

    {

        normalize = 1;

    } else if (name->len == sizeof("Accept-Language") - 1

        && ngx_strncasecmp(name->data, (u_char *) "Accept-Language",

                           sizeof("Accept-Language") - 1) == 0)

    {

        normalize = 1;

    }

    part = &r->headers_in.headers.part;

    header = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            header = part->elts;

            i = 0;

        }

        if (header[i].hash == 0) {

            continue;

        }

        if (header[i].key.len != name->len) {

            continue;

        }

        if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {

            continue;

        }

        if (!normalize) {

            if (multiple) {

                ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);

            }

            ngx_md5_update(md5, header[i].value.data, header[i].value.len);

            multiple = 1;

            continue;

        }

        /* normalize spaces */

        p = header[i].value.data;

        last = p + header[i].value.len;

        while (p < last) {

            while (p < last && (*p == ' ' || *p == ',')) { p++; }

            start = p;

            while (p < last && *p != ',' && *p != ' ') { p++; }

            len = p - start;

            if (len == 0) {

                break;

            }

            if (multiple) {

                ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);

            }

            ngx_md5_update(md5, start, len);

            multiple = 1;

        }

    }

}

static ngx_int_t

‌ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)

{

    ngx_http_file_cache_t  *cache;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,

                   "http file cache reopen");

    if (c->secondary) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      "cache file \"%s\" has incorrect vary hash",

                      c->file.name.data);

        return NGX_DECLINED;

    }

    cache = c->file_cache;

    ngx_shmtx_lock(&cache->shpool->mutex);

    c->node->count--;

    c->node = NULL;

    ngx_shmtx_unlock(&cache->shpool->mutex);

    c->secondary = 1;

    c->file.name.len = 0;

    c->body_start = c->buf->end - c->buf->start;

    ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);

    return ngx_http_file_cache_open(r);

}

void

‌ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)

{

    ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;

    u_char            *p;

    ngx_str_t         *key;

    ngx_uint_t         i;

    ngx_http_cache_t  *c;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache set header");

    c = r->cache;

    ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));

    h->version = NGX_HTTP_CACHE_VERSION;

    h->valid_sec = c->valid_sec;

    h->last_modified = c->last_modified;

    h->date = c->date;

    h->crc32 = c->crc32;

    h->valid_msec = (u_short) c->valid_msec;

    h->header_start = (u_short) c->header_start;

    h->body_start = (u_short) c->body_start;

    if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {

        h->etag_len = (u_char) c->etag.len;

        ngx_memcpy(h->etag, c->etag.data, c->etag.len);

    }

    if (c->vary.len) {

        if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {

            /* should not happen */

            c->vary.len = NGX_HTTP_CACHE_VARY_LEN;

        }

        h->vary_len = (u_char) c->vary.len;

        ngx_memcpy(h->vary, c->vary.data, c->vary.len);

        ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);

        ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);

    } else {

        ngx_memzero(c->variant, NGX_HTTP_CACHE_KEY_LEN);

    }

    p = buf + sizeof(ngx_http_file_cache_header_t);

    p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));

    key = c->keys.elts;

    for (i = 0; i < c->keys.nelts; i++) {

        p = ngx_copy(p, key[i].data, key[i].len);

    }

    *p = LF;

}

void

‌ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)

{

    off_t                   fs_size;

    ngx_int_t               rc;

    ngx_file_uniq_t         uniq;

    ngx_file_info_t         fi;

    ngx_http_cache_t        *c;

    ngx_ext_rename_file_t   ext;

    ngx_http_file_cache_t  *cache;

    c = r->cache;

    if (c->updated) {

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache update");

    cache = c->file_cache;

    if (c->secondary

        && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) != 0)

    {

        /*

         * if the variant hash doesn't match one we used as a secondary

         * cache key, switch back to the original key

         */

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http file cache main key");

        ngx_shmtx_lock(&cache->shpool->mutex);

        c->node->count--;

        c->node->updating = 0;

        c->node = NULL;

        ngx_shmtx_unlock(&cache->shpool->mutex);

        c->file.name.len = 0;

        ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);

        if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {

            return;

        }

        if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {

            return;

        }

    }

    c->updated = 1;

    c->updating = 0;

    uniq = 0;

    fs_size = 0;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache rename: \"%s\" to \"%s\"",

                   tf->file.name.data, c->file.name.data);

    ext.access = NGX_FILE_OWNER_ACCESS;

    ext.path_access = NGX_FILE_OWNER_ACCESS;

    ext.time = -1;

    ext.create_path = 1;

    ext.delete_file = 1;

    ext.log = r->connection->log;

    rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);

    if (rc == NGX_OK) {

        if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                          ngx_fd_info_n " \"%s\" failed", tf->file.name.data);

            rc = NGX_ERROR;

        } else {

            uniq = ngx_file_uniq(&fi);

            fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;

        }

    }

    ngx_shmtx_lock(&cache->shpool->mutex);

    c->node->count--;

    c->node->uniq = uniq;

    c->node->body_start = c->body_start;

    cache->sh->size += fs_size - c->node->fs_size;

    c->node->fs_size = fs_size;

    if (rc == NGX_OK) {

        c->node->exists = 1;

    }

    c->node->updating = 0;

    ngx_shmtx_unlock(&cache->shpool->mutex);

}

void

‌ngx_http_file_cache_update_header(ngx_http_request_t *r)

{

    ssize_t                        n;

    ngx_err_t                      err;

    ngx_file_t                     file;

    ngx_file_info_t                fi;

    ngx_http_cache_t              *c;

    ngx_http_file_cache_header_t   h;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache update header");

    c = r->cache;

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = c->file.name;

    file.log = r->connection->log;

    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

    if (file.fd == NGX_INVALID_FILE) {

        err = ngx_errno;

        /* cache file may have been deleted */

        if (err == NGX_ENOENT) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http file cache \"%s\" not found",

                           file.name.data);

            return;

        }

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,

                      ngx_open_file_n " \"%s\" failed", file.name.data);

        return;

    }

    /*

     * make sure cache file wasn't replaced;

     * if it was, do nothing

     */

    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                      ngx_fd_info_n " \"%s\" failed", file.name.data);

        goto done;

    }

    if (c->uniq != ngx_file_uniq(&fi)

        || c->length != ngx_file_size(&fi))

    {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http file cache \"%s\" changed",

                       file.name.data);

        goto done;

    }

    n = ngx_read_file(&file, (u_char *) &h,

                      sizeof(ngx_http_file_cache_header_t), 0);

    if (n == NGX_ERROR) {

        goto done;

    }

    if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,

                      ngx_read_file_n " read only %z of %z from \"%s\"",

                      n, sizeof(ngx_http_file_cache_header_t), file.name.data);

        goto done;

    }

    if (h.version != NGX_HTTP_CACHE_VERSION

        || h.last_modified != c->last_modified

        || h.crc32 != c->crc32

        || h.header_start != c->header_start

        || h.body_start != c->body_start)

    {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http file cache \"%s\" content changed",

                       file.name.data);

        goto done;

    }

    /*

     * update cache file header with new data,

     * notably h.valid_sec and h.date

     */

    ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));

    h.version = NGX_HTTP_CACHE_VERSION;

    h.valid_sec = c->valid_sec;

    h.last_modified = c->last_modified;

    h.date = c->date;

    h.crc32 = c->crc32;

    h.valid_msec = (u_short) c->valid_msec;

    h.header_start = (u_short) c->header_start;

    h.body_start = (u_short) c->body_start;

    if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {

        h.etag_len = (u_char) c->etag.len;

        ngx_memcpy(h.etag, c->etag.data, c->etag.len);

    }

    if (c->vary.len) {

        if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {

            /* should not happen */

            c->vary.len = NGX_HTTP_CACHE_VARY_LEN;

        }

        h.vary_len = (u_char) c->vary.len;

        ngx_memcpy(h.vary, c->vary.data, c->vary.len);

        ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);

        ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);

    }

    (void) ngx_write_file(&file, (u_char *) &h,

                          sizeof(ngx_http_file_cache_header_t), 0);

done:

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", file.name.data);

    }

}

ngx_int_t

‌ngx_http_cache_send(ngx_http_request_t *r)

{

    ngx_int_t          rc;

    ngx_buf_t         *b;

    ngx_chain_t        out;

    ngx_http_cache_t  *c;

    c = r->cache;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http file cache send: %s", c->file.name.data);

    if (r != r->main && c->length - c->body_start == 0) {

        return ngx_http_send_header(r);

    }

    /* 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 = c->body_start;

    b->file_last = c->length;

    b->in_file = (c->length - c->body_start) ? 1: 0;

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

    b->last_in_chain = 1;

    b->file->fd = c->file.fd;

    b->file->name = c->file.name;

    b->file->log = r->connection->log;

    out.buf = b;

    out.next = NULL;

    return ngx_http_output_filter(r, &out);

}

void

‌ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)

{

    ngx_http_file_cache_t       *cache;

    ngx_http_file_cache_node_t  *fcn;

    if (c->updated || c->node == NULL) {

        return;

    }

    cache = c->file_cache;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,

                   "http file cache free, fd: %d", c->file.fd);

    ngx_shmtx_lock(&cache->shpool->mutex);

    fcn = c->node;

    fcn->count--;

    if (c->updating && fcn->lock_time == c->lock_time) {

        fcn->updating = 0;

    }

    if (c->error) {

        fcn->error = c->error;

        if (c->valid_sec) {

            fcn->valid_sec = c->valid_sec;

            fcn->valid_msec = c->valid_msec;

        }

    } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {

        ngx_queue_remove(&fcn->queue);

        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);

        ngx_slab_free_locked(cache->shpool, fcn);

        c->node = NULL;

    }

    ngx_shmtx_unlock(&cache->shpool->mutex);

    c->updated = 1;

    c->updating = 0;

    if (c->temp_file) {

        if (tf && tf->file.fd != NGX_INVALID_FILE) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,

                           "http file cache incomplete: \"%s\"",

                           tf->file.name.data);

            if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,

                              ngx_delete_file_n " \"%s\" failed",

                              tf->file.name.data);

            }

        }

    }

    if (c->wait_event.timer_set) {

        ngx_del_timer(&c->wait_event);

    }

}

static void

‌ngx_http_file_cache_cleanup(void *data)

{

    ngx_http_cache_t  *c = data;

    if (c->updated) {

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,

                   "http file cache cleanup");

    if (c->updating) {

        ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,

                      "stalled cache updating, error:%ui", c->error);

    }

    ngx_http_file_cache_free(c, NULL);

}

static time_t

‌ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)

{

    u_char                      *name;

    size_t                       len;

    time_t                       wait;

    ngx_uint_t                   tries;

    ngx_path_t                  *path;

    ngx_queue_t                 *q;

    ngx_http_file_cache_node_t  *fcn;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                   "http file cache forced expire");

    path = cache->path;

    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

    name = ngx_alloc(len + 1, ngx_cycle->log);

    if (name == NULL) {

        return 10;

    }

    ngx_memcpy(name, path->name.data, path->name.len);

    wait = 10;

    tries = 20;

    ngx_shmtx_lock(&cache->shpool->mutex);

    for (q = ngx_queue_last(&cache->sh->queue);

         q != ngx_queue_sentinel(&cache->sh->queue);

         q = ngx_queue_prev(q))

    {

        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                  "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",

                  fcn->count, fcn->exists,

                  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);

        if (fcn->count == 0) {

            ngx_http_file_cache_delete(cache, q, name);

            wait = 0;

        } else {

            if (--tries) {

                continue;

            }

            wait = 1;

        }

        break;

    }

    ngx_shmtx_unlock(&cache->shpool->mutex);

    ngx_free(name);

    return wait;

}

static time_t

‌ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)

{

    u_char                      *name, *p;

    size_t                       len;

    time_t                       now, wait;

    ngx_path_t                  *path;

    ngx_queue_t                 *q;

    ngx_http_file_cache_node_t  *fcn;

    u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                   "http file cache expire");

    path = cache->path;

    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

    name = ngx_alloc(len + 1, ngx_cycle->log);

    if (name == NULL) {

        return 10;

    }

    ngx_memcpy(name, path->name.data, path->name.len);

    now = ngx_time();

    ngx_shmtx_lock(&cache->shpool->mutex);

    for ( ;; ) {

        if (ngx_quit || ngx_terminate) {

            wait = 1;

            break;

        }

        if (ngx_queue_empty(&cache->sh->queue)) {

            wait = 10;

            break;

        }

        q = ngx_queue_last(&cache->sh->queue);

        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

        wait = fcn->expire - now;

        if (wait > 0) {

            wait = wait > 10 ? 10 : wait;

            break;

        }

        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                       "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",

                       fcn->count, fcn->exists,

                       fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);

        if (fcn->count == 0) {

            ngx_http_file_cache_delete(cache, q, name);

            continue;

        }

        if (fcn->deleting) {

            wait = 1;

            break;

        }

        p = ngx_hex_dump(key, (u_char *) &fcn->node.key,

                         sizeof(ngx_rbtree_key_t));

        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);

        (void) ngx_hex_dump(p, fcn->key, len);

        /*

         * abnormally exited workers may leave locked cache entries,

         * and although it may be safe to remove them completely,

         * we prefer to just move them to the top of the inactive queue

         */

        ngx_queue_remove(q);

        fcn->expire = ngx_time() + cache->inactive;

        ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                      "ignore long locked inactive cache entry %*s, count:%d",

                      2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);

    }

    ngx_shmtx_unlock(&cache->shpool->mutex);

    ngx_free(name);

    return wait;

}

static void

‌ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,

    u_char *name)

{

    u_char                      *p;

    size_t                       len;

    ngx_path_t                  *path;

    ngx_http_file_cache_node_t  *fcn;

    fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

    if (fcn->exists) {

        cache->sh->size -= fcn->fs_size;

        path = cache->path;

        p = name + path->name.len + 1 + path->len;

        p = ngx_hex_dump(p, (u_char *) &fcn->node.key,

                         sizeof(ngx_rbtree_key_t));

        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);

        p = ngx_hex_dump(p, fcn->key, len);

        *p = '\0';

        fcn->count++;

        fcn->deleting = 1;

        ngx_shmtx_unlock(&cache->shpool->mutex);

        len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

        ngx_create_hashed_filename(path, name, len);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                       "http file cache expire: \"%s\"", name);

        if (ngx_delete_file(name) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,

                          ngx_delete_file_n " \"%s\" failed", name);

        }

        ngx_shmtx_lock(&cache->shpool->mutex);

        fcn->count--;

        fcn->deleting = 0;

    }

    if (fcn->count == 0) {

        ngx_queue_remove(q);

        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);

        ngx_slab_free_locked(cache->shpool, fcn);

    }

}

static time_t

‌ngx_http_file_cache_manager(void *data)

{

    ngx_http_file_cache_t  *cache = data;

    off_t   size;

    time_t  next, wait;

    next = ngx_http_file_cache_expire(cache);

    cache->last = ngx_current_msec;

    cache->files = 0;

    for ( ;; ) {

        ngx_shmtx_lock(&cache->shpool->mutex);

        size = cache->sh->size;

        ngx_shmtx_unlock(&cache->shpool->mutex);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                       "http file cache size: %O", size);

        if (size < cache->max_size) {

            return next;

        }

        wait = ngx_http_file_cache_forced_expire(cache);

        if (wait > 0) {

            return wait;

        }

        if (ngx_quit || ngx_terminate) {

            return next;

        }

    }

}

static void

‌ngx_http_file_cache_loader(void *data)

{

    ngx_http_file_cache_t  *cache = data;

    ngx_tree_ctx_t  tree;

    if (!cache->sh->cold || cache->sh->loading) {

        return;

    }

    if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                   "http file cache loader");

    tree.init_handler = NULL;

    tree.file_handler = ngx_http_file_cache_manage_file;

    tree.pre_tree_handler = ngx_http_file_cache_noop;

    tree.post_tree_handler = ngx_http_file_cache_noop;

    tree.spec_handler = ngx_http_file_cache_delete_file;

    tree.data = cache;

    tree.alloc = 0;

    tree.log = ngx_cycle->log;

    cache->last = ngx_current_msec;

    cache->files = 0;

    if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {

        cache->sh->loading = 0;

        return;

    }

    cache->sh->cold = 0;

    cache->sh->loading = 0;

    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,

                  "http file cache: %V %.3fM, bsize: %uz",

                  &cache->path->name,

                  ((double) cache->sh->size * cache->bsize) / (1024 * 1024),

                  cache->bsize);

}

static ngx_int_t

‌ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    ngx_msec_t              elapsed;

    ngx_http_file_cache_t  *cache;

    cache = ctx->data;

    if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {

        (void) ngx_http_file_cache_delete_file(ctx, path);

    }

    if (++cache->files >= cache->loader_files) {

        ngx_http_file_cache_loader_sleep(cache);

    } else {

        ngx_time_update();

        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,

                       "http file cache loader time elapsed: %M", elapsed);

        if (elapsed >= cache->loader_threshold) {

            ngx_http_file_cache_loader_sleep(cache);

        }

    }

    return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;

}

static void

‌ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)

{

    ngx_msleep(cache->loader_sleep);

    ngx_time_update();

    cache->last = ngx_current_msec;

    cache->files = 0;

}

static ngx_int_t

‌ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)

{

    u_char                 *p;

    ngx_int_t               n;

    ngx_uint_t              i;

    ngx_http_cache_t        c;

    ngx_http_file_cache_t  *cache;

    if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {

        return NGX_ERROR;

    }

    if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {

        ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,

                      "cache file \"%s\" is too small", name->data);

        return NGX_ERROR;

    }

    ngx_memzero(&c, sizeof(ngx_http_cache_t));

    cache = ctx->data;

    c.length = ctx->size;

    c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;

    p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];

    for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {

        n = ngx_hextoi(p, 2);

        if (n == NGX_ERROR) {

            return NGX_ERROR;

        }

        p += 2;

        c.key[i] = (u_char) n;

    }

    return ngx_http_file_cache_add(cache, &c);

}

static ngx_int_t

‌ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)

{

    ngx_http_file_cache_node_t  *fcn;

    ngx_shmtx_lock(&cache->shpool->mutex);

    fcn = ngx_http_file_cache_lookup(cache, c->key);

    if (fcn == NULL) {

        fcn = ngx_slab_calloc_locked(cache->shpool,

                                     sizeof(ngx_http_file_cache_node_t));

        if (fcn == NULL) {

            ngx_shmtx_unlock(&cache->shpool->mutex);

            return NGX_ERROR;

        }

        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],

                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

        ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

        fcn->uses = 1;

        fcn->exists = 1;

        fcn->fs_size = c->fs_size;

        cache->sh->size += c->fs_size;

    } else {

        ngx_queue_remove(&fcn->queue);

    }

    fcn->expire = ngx_time() + cache->inactive;

    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

    ngx_shmtx_unlock(&cache->shpool->mutex);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http file cache delete: \"%s\"", path->data);

    if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                      ngx_delete_file_n " \"%s\" failed", path->data);

    }

    return NGX_OK;

}

time_t

‌ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)

{

    ngx_uint_t               i;

    ngx_http_cache_valid_t  *valid;

    if (cache_valid == NULL) {

        return 0;

    }

    valid = cache_valid->elts;

    for (i = 0; i < cache_valid->nelts; i++) {

        if (valid[i].status == 0) {

            return valid[i].valid;

        }

        if (valid[i].status == status) {

            return valid[i].valid;

        }

    }

    return 0;

}

char *

‌ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *confp = conf;

    off_t                   max_size;

    u_char                 *last, *p;

    time_t                  inactive;

    ssize_t                 size;

    ngx_str_t               s, name, *value;

    ngx_int_t               loader_files;

    ngx_msec_t              loader_sleep, loader_threshold;

    ngx_uint_t              i, n;

    ngx_array_t            *caches;

    ngx_http_file_cache_t  *cache, **ce;

    cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));

    if (cache == NULL) {

        return NGX_CONF_ERROR;

    }

    cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));

    if (cache->path == NULL) {

        return NGX_CONF_ERROR;

    }

    inactive = 600;

    loader_files = 100;

    loader_sleep = 50;

    loader_threshold = 200;

    name.len = 0;

    size = 0;

    max_size = NGX_MAX_OFF_T_VALUE;

    value = cf->args->elts;

    cache->path->name = value[1];

    if (cache->path->name.data[cache->path->name.len - 1] == '/') {

        cache->path->name.len--;

    }

    if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {

            p = value[i].data + 7;

            last = value[i].data + value[i].len;

            for (n = 0; n < 3 && p < last; n++) {

                if (*p > '0' && *p < '3') {

                    cache->path->level[n] = *p++ - '0';

                    cache->path->len += cache->path->level[n] + 1;

                    if (p == last) {

                        break;

                    }

                    if (*p++ == ':' && n < 2 && p != last) {

                        continue;

                    }

                    goto invalid_levels;

                }

                goto invalid_levels;

            }

            if (cache->path->len < 10 + 3) {

                continue;

            }

        invalid_levels:

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid \"levels\" \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {

            name.data = value[i].data + 10;

            p = (u_char *) ngx_strchr(name.data, ':');

            if (p) {

                name.len = p - name.data;

                p++;

                s.len = value[i].data + value[i].len - p;

                s.data = p;

                size = ngx_parse_size(&s);

                if (size > 8191) {

                    continue;

                }

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid keys zone size \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {

            s.len = value[i].len - 9;

            s.data = value[i].data + 9;

            inactive = ngx_parse_time(&s, 1);

            if (inactive == (time_t) NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid inactive value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {

            s.len = value[i].len - 9;

            s.data = value[i].data + 9;

            max_size = ngx_parse_offset(&s);

            if (max_size < 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid max_size value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {

            loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);

            if (loader_files == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid loader_files value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {

            s.len = value[i].len - 13;

            s.data = value[i].data + 13;

            loader_sleep = ngx_parse_time(&s, 0);

            if (loader_sleep == (ngx_msec_t) NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid loader_sleep value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {

            s.len = value[i].len - 17;

            s.data = value[i].data + 17;

            loader_threshold = ngx_parse_time(&s, 0);

            if (loader_threshold == (ngx_msec_t) NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid loader_threshold value \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (name.len == 0 || size == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%V\" must have \"keys_zone\" parameter",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    cache->path->manager = ngx_http_file_cache_manager;

    cache->path->loader = ngx_http_file_cache_loader;

    cache->path->data = cache;

    cache->path->conf_file = cf->conf_file->file.name.data;

    cache->path->line = cf->conf_file->line;

    cache->loader_files = loader_files;

    cache->loader_sleep = loader_sleep;

    cache->loader_threshold = loader_threshold;

    if (ngx_add_path(cf, &cache->path) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);

    if (cache->shm_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    if (cache->shm_zone->data) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "duplicate zone \"%V\"", &name);

        return NGX_CONF_ERROR;

    }

    cache->shm_zone->init = ngx_http_file_cache_init;

    cache->shm_zone->data = cache;

    cache->inactive = inactive;

    cache->max_size = max_size;

    caches = (ngx_array_t *) (confp + cmd->offset);

    ce = ngx_array_push(caches);

    if (ce == NULL) {

        return NGX_CONF_ERROR;

    }

    *ce = cache;

    return NGX_CONF_OK;

}

char *

‌ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf)

{

    char  *p = conf;

    time_t                    valid;

    ngx_str_t                *value;

    ngx_uint_t                i, n, status;

    ngx_array_t             **a;

    ngx_http_cache_valid_t   *v;

    static ngx_uint_t         statuses[] = { 200, 301, 302 };

    a = (ngx_array_t **) (p + cmd->offset);

    if (*a == NGX_CONF_UNSET_PTR) {

        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));

        if (*a == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    n = cf->args->nelts - 1;

    valid = ngx_parse_time(&value[n], 1);

    if (valid == (time_t) NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid time value \"%V\"", &value[n]);

        return NGX_CONF_ERROR;

    }

    if (n == 1) {

        for (i = 0; i < 3; i++) {

            v = ngx_array_push(*a);

            if (v == NULL) {

                return NGX_CONF_ERROR;

            }

            v->status = statuses[i];

            v->valid = valid;

        }

        return NGX_CONF_OK;

    }

    for (i = 1; i < n; i++) {

        if (ngx_strcmp(value[i].data, "any") == 0) {

            status = 0;

        } else {

            status = ngx_atoi(value[i].data, value[i].len);

            if (status < 100) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid status \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

        }

        v = ngx_array_push(*a);

        if (v == NULL) {

            return NGX_CONF_ERROR;

        }

        v->status = status;

        v->valid = valid;

    }

    return NGX_CONF_OK;

}

src/http/ngx_http_cache.h - nginx-1.7.9

Data types defined

ngx_http_cache_s

ngx_http_cache_valid_t

ngx_http_file_cache_header_t

ngx_http_file_cache_node_t

ngx_http_file_cache_s

ngx_http_file_cache_sh_t

Macros defined

NGX_HTTP_CACHE_BYPASS

NGX_HTTP_CACHE_ETAG_LEN

NGX_HTTP_CACHE_EXPIRED

NGX_HTTP_CACHE_HIT

NGX_HTTP_CACHE_KEY_LEN

NGX_HTTP_CACHE_MISS

NGX_HTTP_CACHE_REVALIDATED

NGX_HTTP_CACHE_SCARCE

NGX_HTTP_CACHE_STALE

NGX_HTTP_CACHE_UPDATING

NGX_HTTP_CACHE_VARY_LEN

NGX_HTTP_CACHE_VERSION

_NGX_HTTP_CACHE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_CACHE_H_INCLUDED_

‌#define _NGX_HTTP_CACHE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_CACHE_MISS          1

‌#define NGX_HTTP_CACHE_BYPASS        2

‌#define NGX_HTTP_CACHE_EXPIRED       3

‌#define NGX_HTTP_CACHE_STALE         4

‌#define NGX_HTTP_CACHE_UPDATING      5

‌#define NGX_HTTP_CACHE_REVALIDATED   6

‌#define NGX_HTTP_CACHE_HIT           7

‌#define NGX_HTTP_CACHE_SCARCE        8

‌#define NGX_HTTP_CACHE_KEY_LEN       16

‌#define NGX_HTTP_CACHE_ETAG_LEN      42

‌#define NGX_HTTP_CACHE_VARY_LEN      42

‌#define NGX_HTTP_CACHE_VERSION       3

typedef struct {

    ngx_uint_t                       status;

    time_t                           valid;

‌} ngx_http_cache_valid_t;

typedef struct {

    ngx_rbtree_node_t                node;

    ngx_queue_t                      queue;

    u_char                           key[NGX_HTTP_CACHE_KEY_LEN

                                         - sizeof(ngx_rbtree_key_t)];

    unsigned                         count:20;

    unsigned                         uses:10;

    unsigned                         valid_msec:10;

    unsigned                         error:10;

    unsigned                         exists:1;

    unsigned                         updating:1;

    unsigned                         deleting:1;

                                     /* 11 unused bits */

    ngx_file_uniq_t                  uniq;

    time_t                           expire;

    time_t                           valid_sec;

    size_t                           body_start;

    off_t                            fs_size;

    ngx_msec_t                       lock_time;

‌} ngx_http_file_cache_node_t;

‌struct ngx_http_cache_s {

    ngx_file_t                       file;

    ngx_array_t                      keys;

    uint32_t                         crc32;

    u_char                           key[NGX_HTTP_CACHE_KEY_LEN];

    u_char                           main[NGX_HTTP_CACHE_KEY_LEN];

    ngx_file_uniq_t                  uniq;

    time_t                           valid_sec;

    time_t                           last_modified;

    time_t                           date;

    ngx_str_t                        etag;

    ngx_str_t                        vary;

    u_char                           variant[NGX_HTTP_CACHE_KEY_LEN];

    size_t                           header_start;

    size_t                           body_start;

    off_t                            length;

    off_t                            fs_size;

    ngx_uint_t                       min_uses;

    ngx_uint_t                       error;

    ngx_uint_t                       valid_msec;

    ngx_buf_t                       *buf;

    ngx_http_file_cache_t           *file_cache;

    ngx_http_file_cache_node_t      *node;

    ngx_msec_t                       lock_timeout;

    ngx_msec_t                       lock_age;

    ngx_msec_t                       lock_time;

    ngx_msec_t                       wait_time;

    ngx_event_t                      wait_event;

    unsigned                         lock:1;

    unsigned                         waiting:1;

    unsigned                         updated:1;

    unsigned                         updating:1;

    unsigned                         exists:1;

    unsigned                         temp_file:1;

    unsigned                         reading:1;

    unsigned                         secondary:1;

};

typedef struct {

    ngx_uint_t                       version;

    time_t                           valid_sec;

    time_t                           last_modified;

    time_t                           date;

    uint32_t                         crc32;

    u_short                          valid_msec;

    u_short                          header_start;

    u_short                          body_start;

    u_char                           etag_len;

    u_char                           etag[NGX_HTTP_CACHE_ETAG_LEN];

    u_char                           vary_len;

    u_char                           vary[NGX_HTTP_CACHE_VARY_LEN];

    u_char                           variant[NGX_HTTP_CACHE_KEY_LEN];

‌} ngx_http_file_cache_header_t;

typedef struct {

    ngx_rbtree_t                     rbtree;

    ngx_rbtree_node_t                sentinel;

    ngx_queue_t                      queue;

    ngx_atomic_t                     cold;

    ngx_atomic_t                     loading;

    off_t                            size;

‌} ngx_http_file_cache_sh_t;

‌struct ngx_http_file_cache_s {

    ngx_http_file_cache_sh_t        *sh;

    ngx_slab_pool_t                 *shpool;

    ngx_path_t                      *path;

    off_t                            max_size;

    size_t                           bsize;

    time_t                           inactive;

    ngx_uint_t                       files;

    ngx_uint_t                       loader_files;

    ngx_msec_t                       last;

    ngx_msec_t                       loader_sleep;

    ngx_msec_t                       loader_threshold;

    ngx_shm_zone_t                  *shm_zone;

};

ngx_int_t ngx_http_file_cache_new(ngx_http_request_t *r);

ngx_int_t ngx_http_file_cache_create(ngx_http_request_t *r);

void ngx_http_file_cache_create_key(ngx_http_request_t *r);

ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r);

void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf);

void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf);

void ngx_http_file_cache_update_header(ngx_http_request_t *r);

ngx_int_t ngx_http_cache_send(ngx_http_request_t *);

void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf);

time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);

char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

extern ngx_str_t  ngx_http_cache_status[];

#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */

src/os/unix/ngx_file_aio_read.c - nginx-1.7.9

Functions defined

ngx_file_aio_event_handler

ngx_file_aio_read

ngx_file_aio_result

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

/*

* FreeBSD file AIO features and quirks:

*

*    if an asked data are already in VM cache, then aio_error() returns 0,

*    and the data are already copied in buffer;

*

*    aio_read() preread in VM cache as minimum 16K (probably BKVASIZE);

*    the first AIO preload may be up to 128K;

*

*    aio_read/aio_error() may return EINPROGRESS for just written data;

*

*    kqueue EVFILT_AIO filter is level triggered only: an event repeats

*    until aio_return() will be called;

*

*    aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always.

*/

extern int  ngx_kqueue;

static ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio,

    ngx_event_t *ev);

static void ngx_file_aio_event_handler(ngx_event_t *ev);

ssize_t

‌ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,

    ngx_pool_t *pool)

{

    int               n;

    ngx_event_t      *ev;

    ngx_event_aio_t  *aio;

    if (!ngx_file_aio) {

        return ngx_read_file(file, buf, size, offset);

    }

    aio = file->aio;

    if (aio == NULL) {

        aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));

        if (aio == NULL) {

            return NGX_ERROR;

        }

        aio->file = file;

        aio->fd = file->fd;

        aio->event.data = aio;

        aio->event.ready = 1;

        aio->event.log = file->log;

#if (NGX_HAVE_AIO_SENDFILE)

        aio->last_offset = -1;

#endif

        file->aio = aio;

    }

    ev = &aio->event;

    if (!ev->ready) {

        ngx_log_error(NGX_LOG_ALERT, file->log, 0,

                      "second aio post for \"%V\"", &file->name);

        return NGX_AGAIN;

    }

    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "aio complete:%d @%O:%z %V",

                   ev->complete, offset, size, &file->name);

    if (ev->complete) {

        ev->complete = 0;

        ngx_set_errno(aio->err);

        if (aio->err == 0) {

            return aio->nbytes;

        }

        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                      "aio read \"%s\" failed", file->name.data);

        return NGX_ERROR;

    }

    ngx_memzero(&aio->aiocb, sizeof(struct aiocb));

    aio->aiocb.aio_fildes = file->fd;

    aio->aiocb.aio_offset = offset;

    aio->aiocb.aio_buf = buf;

    aio->aiocb.aio_nbytes = size;

#if (NGX_HAVE_KQUEUE)

    aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;

    aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;

    aio->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;

#endif

    ev->handler = ngx_file_aio_event_handler;

    n = aio_read(&aio->aiocb);

    if (n == -1) {

        n = ngx_errno;

        if (n == NGX_EAGAIN) {

            return ngx_read_file(file, buf, size, offset);

        }

        ngx_log_error(NGX_LOG_CRIT, file->log, n,

                      "aio_read(\"%V\") failed", &file->name);

        if (n == NGX_ENOSYS) {

            ngx_file_aio = 0;

            return ngx_read_file(file, buf, size, offset);

        }

        return NGX_ERROR;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "aio_read: fd:%d %d", file->fd, n);

    ev->active = 1;

    ev->ready = 0;

    ev->complete = 0;

    return ngx_file_aio_result(aio->file, aio, ev);

}

static ssize_t

‌ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev)

{

    int        n;

    ngx_err_t  err;

    n = aio_error(&aio->aiocb);

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "aio_error: fd:%d %d", file->fd, n);

    if (n == -1) {

        err = ngx_errno;

        aio->err = err;

        ngx_log_error(NGX_LOG_ALERT, file->log, err,

                      "aio_error(\"%V\") failed", &file->name);

        return NGX_ERROR;

    }

    if (n == NGX_EINPROGRESS) {

        if (ev->ready) {

            ev->ready = 0;

            ngx_log_error(NGX_LOG_ALERT, file->log, n,

                          "aio_read(\"%V\") still in progress",

                          &file->name);

        }

        return NGX_AGAIN;

    }

    n = aio_return(&aio->aiocb);

    if (n == -1) {

        err = ngx_errno;

        aio->err = err;

        ev->ready = 1;

        ngx_log_error(NGX_LOG_CRIT, file->log, err,

                      "aio_return(\"%V\") failed", &file->name);

        return NGX_ERROR;

    }

    aio->err = 0;

    aio->nbytes = n;

    ev->ready = 1;

    ev->active = 0;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "aio_return: fd:%d %d", file->fd, n);

    return n;

}

static void

‌ngx_file_aio_event_handler(ngx_event_t *ev)

{

    ngx_event_aio_t  *aio;

    aio = ev->data;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,

                   "aio event handler fd:%d %V", aio->fd, &aio->file->name);

    if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) {

        aio->handler(ev);

    }

}

src/event/modules/ngx_epoll_module.c - nginx-1.7.9

Global variables defined

ep

epoll_name

event_list

nevents

ngx_aio_ctx

ngx_epoll_commands

ngx_epoll_module

ngx_epoll_module_ctx

ngx_eventfd

ngx_eventfd_conn

ngx_eventfd_event

Data types defined

aio_context_t

epoll_data

epoll_data_t

epoll_event

io_event

ngx_epoll_conf_t

Functions defined

epoll_create

epoll_ctl

epoll_wait

io_destroy

io_getevents

io_setup

ngx_epoll_add_connection

ngx_epoll_add_event

ngx_epoll_aio_init

ngx_epoll_create_conf

ngx_epoll_del_connection

ngx_epoll_del_event

ngx_epoll_done

ngx_epoll_eventfd_handler

ngx_epoll_init

ngx_epoll_init_conf

ngx_epoll_process_events

Macros defined

EPOLLERR

EPOLLET

EPOLLHUP

EPOLLIN

EPOLLMSG

EPOLLONESHOT

EPOLLOUT

EPOLLPRI

EPOLLRDBAND

EPOLLRDHUP

EPOLLRDNORM

EPOLLWRBAND

EPOLLWRNORM

EPOLL_CTL_ADD

EPOLL_CTL_DEL

EPOLL_CTL_MOD

SYS_eventfd

SYS_io_destroy

SYS_io_getevents

SYS_io_setup

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_TEST_BUILD_EPOLL)

/* epoll declarations */

‌#define EPOLLIN        0x001

‌#define EPOLLPRI       0x002

‌#define EPOLLOUT       0x004

‌#define EPOLLRDNORM    0x040

‌#define EPOLLRDBAND    0x080

‌#define EPOLLWRNORM    0x100

‌#define EPOLLWRBAND    0x200

‌#define EPOLLMSG       0x400

‌#define EPOLLERR       0x008

‌#define EPOLLHUP       0x010

‌#define EPOLLRDHUP     0x2000

‌#define EPOLLET        0x80000000

‌#define EPOLLONESHOT   0x40000000

‌#define EPOLL_CTL_ADD  1

‌#define EPOLL_CTL_DEL  2

‌#define EPOLL_CTL_MOD  3

‌typedef union epoll_data {

    void         *ptr;

    int           fd;

    uint32_t      u32;

    uint64_t      u64;

‌} epoll_data_t;

‌struct epoll_event {

    uint32_t      events;

    epoll_data_t  data;

};

int epoll_create(int size);

‌int epoll_create(int size)

{

    return -1;

}

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

‌int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

{

    return -1;

}

int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout);

‌int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout)

{

    return -1;

}

#if (NGX_HAVE_FILE_AIO)

‌#define SYS_io_setup      245

‌#define SYS_io_destroy    246

‌#define SYS_io_getevents  247

‌#define SYS_eventfd       323

‌typedef u_int  aio_context_t;

‌struct io_event {

    uint64_t  data;  /* the data field from the iocb */

    uint64_t  obj;   /* what iocb this event came from */

    int64_t   res;   /* result code for this event */

    int64_t   res2;  /* secondary result */

};

#endif

#endif

typedef struct {

    ngx_uint_t  events;

    ngx_uint_t  aio_requests;

‌} ngx_epoll_conf_t;

static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_epoll_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c);

static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c,

    ngx_uint_t flags);

static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

#if (NGX_HAVE_FILE_AIO)

static void ngx_epoll_eventfd_handler(ngx_event_t *ev);

#endif

static void *ngx_epoll_create_conf(ngx_cycle_t *cycle);

static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf);

‌static int                  ep = -1;

‌static struct epoll_event  *event_list;

‌static ngx_uint_t           nevents;

#if (NGX_HAVE_FILE_AIO)

‌int                         ngx_eventfd = -1;

‌aio_context_t               ngx_aio_ctx = 0;

‌static ngx_event_t          ngx_eventfd_event;

‌static ngx_connection_t     ngx_eventfd_conn;

#endif

‌static ngx_str_t      epoll_name = ngx_string("epoll");

‌static ngx_command_t  ngx_epoll_commands[] = {

    { ngx_string("epoll_events"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_epoll_conf_t, events),

      NULL },

    { ngx_string("worker_aio_requests"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_epoll_conf_t, aio_requests),

      NULL },

      ngx_null_command

};

‌ngx_event_module_t  ngx_epoll_module_ctx = {

    &epoll_name,

    ngx_epoll_create_conf,               /* create configuration */

    ngx_epoll_init_conf,                 /* init configuration */

    {

        ngx_epoll_add_event,             /* add an event */

        ngx_epoll_del_event,             /* delete an event */

        ngx_epoll_add_event,             /* enable an event */

        ngx_epoll_del_event,             /* disable an event */

        ngx_epoll_add_connection,        /* add an connection */

        ngx_epoll_del_connection,        /* delete an connection */

        NULL,                            /* process the changes */

        ngx_epoll_process_events,        /* process the events */

        ngx_epoll_init,                  /* init the events */

        ngx_epoll_done,                  /* done the events */

    }

};

‌ngx_module_t  ngx_epoll_module = {

    NGX_MODULE_V1,

    &ngx_epoll_module_ctx,               /* module context */

    ngx_epoll_commands,                  /* module directives */

    NGX_EVENT_MODULE,                    /* module type */

    NULL,                                /* init master */

    NULL,                                /* init module */

    NULL,                                /* init process */

    NULL,                                /* init thread */

    NULL,                                /* exit thread */

    NULL,                                /* exit process */

    NULL,                                /* exit master */

    NGX_MODULE_V1_PADDING

};

#if (NGX_HAVE_FILE_AIO)

/*

* We call io_setup(), io_destroy() io_submit(), and io_getevents() directly

* as syscalls instead of libaio usage, because the library header file

* supports eventfd() since 0.3.107 version only.

*/

static int

‌io_setup(u_int nr_reqs, aio_context_t *ctx)

{

    return syscall(SYS_io_setup, nr_reqs, ctx);

}

static int

‌io_destroy(aio_context_t ctx)

{

    return syscall(SYS_io_destroy, ctx);

}

static int

‌io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,

    struct timespec *tmo)

{

    return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);

}

static void

‌ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)

{

    int                 n;

    struct epoll_event  ee;

#if (NGX_HAVE_SYS_EVENTFD_H)

    ngx_eventfd = eventfd(0, 0);

#else

    ngx_eventfd = syscall(SYS_eventfd, 0);

#endif

    if (ngx_eventfd == -1) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                      "eventfd() failed");

        ngx_file_aio = 0;

        return;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "eventfd: %d", ngx_eventfd);

    n = 1;

    if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                      "ioctl(eventfd, FIONBIO) failed");

        goto failed;

    }

    if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                      "io_setup() failed");

        goto failed;

    }

    ngx_eventfd_event.data = &ngx_eventfd_conn;

    ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;

    ngx_eventfd_event.log = cycle->log;

    ngx_eventfd_event.active = 1;

    ngx_eventfd_conn.fd = ngx_eventfd;

    ngx_eventfd_conn.read = &ngx_eventfd_event;

    ngx_eventfd_conn.log = cycle->log;

    ee.events = EPOLLIN|EPOLLET;

    ee.data.ptr = &ngx_eventfd_conn;

    if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {

        return;

    }

    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                  "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");

    if (io_destroy(ngx_aio_ctx) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "io_destroy() failed");

    }

failed:

    if (close(ngx_eventfd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "eventfd close() failed");

    }

    ngx_eventfd = -1;

    ngx_aio_ctx = 0;

    ngx_file_aio = 0;

}

#endif

static ngx_int_t

‌ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    ngx_epoll_conf_t  *epcf;

    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);

    if (ep == -1) {

        ep = epoll_create(cycle->connection_n / 2);

        if (ep == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "epoll_create() failed");

            return NGX_ERROR;

        }

#if (NGX_HAVE_FILE_AIO)

        ngx_epoll_aio_init(cycle, epcf);

#endif

    }

    if (nevents < epcf->events) {

        if (event_list) {

            ngx_free(event_list);

        }

        event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,

                               cycle->log);

        if (event_list == NULL) {

            return NGX_ERROR;

        }

    }

    nevents = epcf->events;

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_epoll_module_ctx.actions;

#if (NGX_HAVE_CLEAR_EVENT)

    ngx_event_flags = NGX_USE_CLEAR_EVENT

#else

    ngx_event_flags = NGX_USE_LEVEL_EVENT

#endif

                      |NGX_USE_GREEDY_EVENT

                      |NGX_USE_EPOLL_EVENT;

    return NGX_OK;

}

static void

‌ngx_epoll_done(ngx_cycle_t *cycle)

{

    if (close(ep) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "epoll close() failed");

    }

    ep = -1;

#if (NGX_HAVE_FILE_AIO)

    if (ngx_eventfd != -1) {

        if (io_destroy(ngx_aio_ctx) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "io_destroy() failed");

        }

        if (close(ngx_eventfd) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "eventfd close() failed");

        }

        ngx_eventfd = -1;

    }

    ngx_aio_ctx = 0;

#endif

    ngx_free(event_list);

    event_list = NULL;

    nevents = 0;

}

static ngx_int_t

‌ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    int                  op;

    uint32_t             events, prev;

    ngx_event_t         *e;

    ngx_connection_t    *c;

    struct epoll_event   ee;

    c = ev->data;

    events = (uint32_t) event;

    if (event == NGX_READ_EVENT) {

        e = c->write;

        prev = EPOLLOUT;

#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)

        events = EPOLLIN|EPOLLRDHUP;

#endif

    } else {

        e = c->read;

        prev = EPOLLIN|EPOLLRDHUP;

#if (NGX_WRITE_EVENT != EPOLLOUT)

        events = EPOLLOUT;

#endif

    }

    if (e->active) {

        op = EPOLL_CTL_MOD;

        events |= prev;

    } else {

        op = EPOLL_CTL_ADD;

    }

    ee.events = events | (uint32_t) flags;

    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "epoll add event: fd:%d op:%d ev:%08XD",

                   c->fd, op, ee.events);

    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                      "epoll_ctl(%d, %d) failed", op, c->fd);

        return NGX_ERROR;

    }

    ev->active = 1;

#if 0

    ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    int                  op;

    uint32_t             prev;

    ngx_event_t         *e;

    ngx_connection_t    *c;

    struct epoll_event   ee;

    /*

     * when the file descriptor is closed, the epoll automatically deletes

     * it from its queue, so we do not need to delete explicitly the event

     * before the closing the file descriptor

     */

    if (flags & NGX_CLOSE_EVENT) {

        ev->active = 0;

        return NGX_OK;

    }

    c = ev->data;

    if (event == NGX_READ_EVENT) {

        e = c->write;

        prev = EPOLLOUT;

    } else {

        e = c->read;

        prev = EPOLLIN|EPOLLRDHUP;

    }

    if (e->active) {

        op = EPOLL_CTL_MOD;

        ee.events = prev | (uint32_t) flags;

        ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

    } else {

        op = EPOLL_CTL_DEL;

        ee.events = 0;

        ee.data.ptr = NULL;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "epoll del event: fd:%d op:%d ev:%08XD",

                   c->fd, op, ee.events);

    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                      "epoll_ctl(%d, %d) failed", op, c->fd);

        return NGX_ERROR;

    }

    ev->active = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_epoll_add_connection(ngx_connection_t *c)

{

    struct epoll_event  ee;

    ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;

    ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "epoll add connection: fd:%d ev:%08XD", c->fd, ee.events);

    if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd);

        return NGX_ERROR;

    }

    c->read->active = 1;

    c->write->active = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)

{

    int                 op;

    struct epoll_event  ee;

    /*

     * when the file descriptor is closed the epoll automatically deletes

     * it from its queue so we do not need to delete explicitly the event

     * before the closing the file descriptor

     */

    if (flags & NGX_CLOSE_EVENT) {

        c->read->active = 0;

        c->write->active = 0;

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "epoll del connection: fd:%d", c->fd);

    op = EPOLL_CTL_DEL;

    ee.events = 0;

    ee.data.ptr = NULL;

    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "epoll_ctl(%d, %d) failed", op, c->fd);

        return NGX_ERROR;

    }

    c->read->active = 0;

    c->write->active = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)

{

    int                events;

    uint32_t           revents;

    ngx_int_t          instance, i;

    ngx_uint_t         level;

    ngx_err_t          err;

    ngx_event_t       *rev, *wev;

    ngx_queue_t       *queue;

    ngx_connection_t  *c;

    /* NGX_TIMER_INFINITE == INFTIM */

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "epoll timer: %M", timer);

    events = epoll_wait(ep, event_list, (int) nevents, timer);

    err = (events == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

        ngx_time_update();

    }

    if (err) {

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {

                ngx_event_timer_alarm = 0;

                return NGX_OK;

            }

            level = NGX_LOG_INFO;

        } else {

            level = NGX_LOG_ALERT;

        }

        ngx_log_error(level, cycle->log, err, "epoll_wait() failed");

        return NGX_ERROR;

    }

    if (events == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "epoll_wait() returned no events without timeout");

        return NGX_ERROR;

    }

    for (i = 0; i < events; i++) {

        c = event_list[i].data.ptr;

        instance = (uintptr_t) c & 1;

        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);

        rev = c->read;

        if (c->fd == -1 || rev->instance != instance) {

            /*

             * the stale event from a file descriptor

             * that was just closed in this iteration

             */

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "epoll: stale event %p", c);

            continue;

        }

        revents = event_list[i].events;

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "epoll: fd:%d ev:%04XD d:%p",

                       c->fd, revents, event_list[i].data.ptr);

        if (revents & (EPOLLERR|EPOLLHUP)) {

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "epoll_wait() error on fd:%d ev:%04XD",

                           c->fd, revents);

        }

#if 0

        if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "strange epoll_wait() events fd:%d ev:%04XD",

                          c->fd, revents);

        }

#endif

        if ((revents & (EPOLLERR|EPOLLHUP))

             && (revents & (EPOLLIN|EPOLLOUT)) == 0)

        {

            /*

             * if the error events were returned without EPOLLIN or EPOLLOUT,

             * then add these flags to handle the events at least in one

             * active handler

             */

            revents |= EPOLLIN|EPOLLOUT;

        }

        if ((revents & EPOLLIN) && rev->active) {

#if (NGX_HAVE_EPOLLRDHUP)

            if (revents & EPOLLRDHUP) {

                rev->pending_eof = 1;

            }

#endif

            rev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                queue = rev->accept ? &ngx_posted_accept_events

                                    : &ngx_posted_events;

                ngx_post_event(rev, queue);

            } else {

                rev->handler(rev);

            }

        }

        wev = c->write;

        if ((revents & EPOLLOUT) && wev->active) {

            if (c->fd == -1 || wev->instance != instance) {

                /*

                 * the stale event from a file descriptor

                 * that was just closed in this iteration

                 */

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "epoll: stale event %p", c);

                continue;

            }

            wev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                ngx_post_event(wev, &ngx_posted_events);

            } else {

                wev->handler(wev);

            }

        }

    }

    return NGX_OK;

}

#if (NGX_HAVE_FILE_AIO)

static void

‌ngx_epoll_eventfd_handler(ngx_event_t *ev)

{

    int               n, events;

    long              i;

    uint64_t          ready;

    ngx_err_t         err;

    ngx_event_t      *e;

    ngx_event_aio_t  *aio;

    struct io_event   event[64];

    struct timespec   ts;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd handler");

    n = read(ngx_eventfd, &ready, 8);

    err = ngx_errno;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd: %d", n);

    if (n != 8) {

        if (n == -1) {

            if (err == NGX_EAGAIN) {

                return;

            }

            ngx_log_error(NGX_LOG_ALERT, ev->log, err, "read(eventfd) failed");

            return;

        }

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "read(eventfd) returned only %d bytes", n);

        return;

    }

    ts.tv_sec = 0;

    ts.tv_nsec = 0;

    while (ready) {

        events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "io_getevents: %l", events);

        if (events > 0) {

            ready -= events;

            for (i = 0; i < events; i++) {

                ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                               "io_event: %uXL %uXL %L %L",

                                event[i].data, event[i].obj,

                                event[i].res, event[i].res2);

                e = (ngx_event_t *) (uintptr_t) event[i].data;

                e->complete = 1;

                e->active = 0;

                e->ready = 1;

                aio = e->data;

                aio->res = event[i].res;

                ngx_post_event(e, &ngx_posted_events);

            }

            continue;

        }

        if (events == 0) {

            return;

        }

        /* events == -1 */

        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                      "io_getevents() failed");

        return;

    }

}

#endif

static void *

‌ngx_epoll_create_conf(ngx_cycle_t *cycle)

{

    ngx_epoll_conf_t  *epcf;

    epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));

    if (epcf == NULL) {

        return NULL;

    }

    epcf->events = NGX_CONF_UNSET;

    epcf->aio_requests = NGX_CONF_UNSET;

    return epcf;

}

static char *

‌ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_epoll_conf_t *epcf = conf;

    ngx_conf_init_uint_value(epcf->events, 512);

    ngx_conf_init_uint_value(epcf->aio_requests, 32);

    return NGX_CONF_OK;

}

src/os/unix/ngx_linux_aio_read.c - nginx-1.7.9

Functions defined

io_submit

ngx_file_aio_event_handler

ngx_file_aio_read

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

extern int            ngx_eventfd;

extern aio_context_t  ngx_aio_ctx;

static void ngx_file_aio_event_handler(ngx_event_t *ev);

static int

‌io_submit(aio_context_t ctx, long n, struct iocb **paiocb)

{

    return syscall(SYS_io_submit, ctx, n, paiocb);

}

ssize_t

‌ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,

    ngx_pool_t *pool)

{

    ngx_err_t         err;

    struct iocb      *piocb[1];

    ngx_event_t      *ev;

    ngx_event_aio_t  *aio;

    if (!ngx_file_aio) {

        return ngx_read_file(file, buf, size, offset);

    }

    aio = file->aio;

    if (aio == NULL) {

        aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));

        if (aio == NULL) {

            return NGX_ERROR;

        }

        aio->file = file;

        aio->fd = file->fd;

        aio->event.data = aio;

        aio->event.ready = 1;

        aio->event.log = file->log;

        file->aio = aio;

    }

    ev = &aio->event;

    if (!ev->ready) {

        ngx_log_error(NGX_LOG_ALERT, file->log, 0,

                      "second aio post for \"%V\"", &file->name);

        return NGX_AGAIN;

    }

    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,

                   "aio complete:%d @%O:%z %V",

                   ev->complete, offset, size, &file->name);

    if (ev->complete) {

        ev->active = 0;

        ev->complete = 0;

        if (aio->res >= 0) {

            ngx_set_errno(0);

            return aio->res;

        }

        ngx_set_errno(-aio->res);

        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,

                      "aio read \"%s\" failed", file->name.data);

        return NGX_ERROR;

    }

    ngx_memzero(&aio->aiocb, sizeof(struct iocb));

    aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev;

    aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD;

    aio->aiocb.aio_fildes = file->fd;

    aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf;

    aio->aiocb.aio_nbytes = size;

    aio->aiocb.aio_offset = offset;

    aio->aiocb.aio_flags = IOCB_FLAG_RESFD;

    aio->aiocb.aio_resfd = ngx_eventfd;

    ev->handler = ngx_file_aio_event_handler;

    piocb[0] = &aio->aiocb;

    if (io_submit(ngx_aio_ctx, 1, piocb) == 1) {

        ev->active = 1;

        ev->ready = 0;

        ev->complete = 0;

        return NGX_AGAIN;

    }

    err = ngx_errno;

    if (err == NGX_EAGAIN) {

        return ngx_read_file(file, buf, size, offset);

    }

    ngx_log_error(NGX_LOG_CRIT, file->log, err,

                  "io_submit(\"%V\") failed", &file->name);

    if (err == NGX_ENOSYS) {

        ngx_file_aio = 0;

        return ngx_read_file(file, buf, size, offset);

    }

    return NGX_ERROR;

}

static void

‌ngx_file_aio_event_handler(ngx_event_t *ev)

{

    ngx_event_aio_t  *aio;

    aio = ev->data;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,

                   "aio event handler fd:%d %V", aio->fd, &aio->file->name);

    aio->handler(ev);

}

src/event/ngx_event_timer.h - nginx-1.7.9

Functions defined

ngx_event_add_timer

ngx_event_del_timer

Macros defined

NGX_TIMER_INFINITE

NGX_TIMER_LAZY_DELAY

_NGX_EVENT_TIMER_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_TIMER_H_INCLUDED_

‌#define _NGX_EVENT_TIMER_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define NGX_TIMER_INFINITE  (ngx_msec_t) -1

‌#define NGX_TIMER_LAZY_DELAY  300

ngx_int_t ngx_event_timer_init(ngx_log_t *log);

ngx_msec_t ngx_event_find_timer(void);

void ngx_event_expire_timers(void);

void ngx_event_cancel_timers(void);

extern ngx_rbtree_t  ngx_event_timer_rbtree;

static ngx_inline void

‌ngx_event_del_timer(ngx_event_t *ev)

{

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "event timer del: %d: %M",

                    ngx_event_ident(ev->data), ev->timer.key);

    ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

#if (NGX_DEBUG)

    ev->timer.left = NULL;

    ev->timer.right = NULL;

    ev->timer.parent = NULL;

#endif

    ev->timer_set = 0;

}

static ngx_inline void

‌ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)

{

    ngx_msec_t      key;

    ngx_msec_int_t  diff;

    key = ngx_current_msec + timer;

    if (ev->timer_set) {

        /*

         * Use a previous timer value if difference between it and a new

         * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows

         * to minimize the rbtree operations for fast connections.

         */

        diff = (ngx_msec_int_t) (key - ev->timer.key);

        if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                           "event timer: %d, old: %M, new: %M",

                            ngx_event_ident(ev->data), ev->timer.key, key);

            return;

        }

        ngx_del_timer(ev);

    }

    ev->timer.key = key;

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "event timer add: %d: %M:%M",

                    ngx_event_ident(ev->data), timer, ev->timer.key);

    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);

    ev->timer_set = 1;

}

#endif /* _NGX_EVENT_TIMER_H_INCLUDED_ */

src/event/ngx_event_timer.c - nginx-1.7.9

Global variables defined

ngx_event_timer_rbtree

ngx_event_timer_sentinel

Functions defined

ngx_event_cancel_timers

ngx_event_expire_timers

ngx_event_find_timer

ngx_event_timer_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌ngx_rbtree_t              ngx_event_timer_rbtree;

‌static ngx_rbtree_node_t  ngx_event_timer_sentinel;

/*

* the event timer rbtree may contain the duplicate keys, however,

* it should not be a problem, because we use the rbtree to find

* a minimum timer value only

*/

ngx_int_t

‌ngx_event_timer_init(ngx_log_t *log)

{

    ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,

                    ngx_rbtree_insert_timer_value);

    return NGX_OK;

}

ngx_msec_t

‌ngx_event_find_timer(void)

{

    ngx_msec_int_t      timer;

    ngx_rbtree_node_t  *node, *root, *sentinel;

    if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {

        return NGX_TIMER_INFINITE;

    }

    root = ngx_event_timer_rbtree.root;

    sentinel = ngx_event_timer_rbtree.sentinel;

    node = ngx_rbtree_min(root, sentinel);

    timer = (ngx_msec_int_t) (node->key - ngx_current_msec);

    return (ngx_msec_t) (timer > 0 ? timer : 0);

}

void

‌ngx_event_expire_timers(void)

{

    ngx_event_t        *ev;

    ngx_rbtree_node_t  *node, *root, *sentinel;

    sentinel = ngx_event_timer_rbtree.sentinel;

    for ( ;; ) {

        root = ngx_event_timer_rbtree.root;

        if (root == sentinel) {

            return;

        }

        node = ngx_rbtree_min(root, sentinel);

        /* node->key > ngx_current_time */

        if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) {

            return;

        }

        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "event timer del: %d: %M",

                       ngx_event_ident(ev->data), ev->timer.key);

        ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

#if (NGX_DEBUG)

        ev->timer.left = NULL;

        ev->timer.right = NULL;

        ev->timer.parent = NULL;

#endif

        ev->timer_set = 0;

        ev->timedout = 1;

        ev->handler(ev);

    }

}

void

‌ngx_event_cancel_timers(void)

{

    ngx_event_t        *ev;

    ngx_rbtree_node_t  *node, *root, *sentinel;

    sentinel = ngx_event_timer_rbtree.sentinel;

    for ( ;; ) {

        root = ngx_event_timer_rbtree.root;

        if (root == sentinel) {

            return;

        }

        node = ngx_rbtree_min(root, sentinel);

        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));

        if (!ev->cancelable) {

            return;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "event timer cancel: %d: %M",

                       ngx_event_ident(ev->data), ev->timer.key);

        ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

#if (NGX_DEBUG)

        ev->timer.left = NULL;

        ev->timer.right = NULL;

        ev->timer.parent = NULL;

#endif

        ev->timer_set = 0;

        ev->handler(ev);

    }

}

src/http/modules/ngx_http_limit_conn_module.c - nginx-1.7.9

Global variables defined

ngx_http_limit_conn_commands

ngx_http_limit_conn_log_levels

ngx_http_limit_conn_module

ngx_http_limit_conn_module_ctx

ngx_http_limit_conn_status_bounds

Data types defined

ngx_http_limit_conn_cleanup_t

ngx_http_limit_conn_conf_t

ngx_http_limit_conn_ctx_t

ngx_http_limit_conn_limit_t

ngx_http_limit_conn_node_t

Functions defined

ngx_http_limit_conn

ngx_http_limit_conn_cleanup

ngx_http_limit_conn_cleanup_all

ngx_http_limit_conn_create_conf

ngx_http_limit_conn_handler

ngx_http_limit_conn_init

ngx_http_limit_conn_init_zone

ngx_http_limit_conn_lookup

ngx_http_limit_conn_merge_conf

ngx_http_limit_conn_rbtree_insert_value

ngx_http_limit_conn_zone

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    u_char                     color;

    u_char                     len;

    u_short                    conn;

    u_char                     data[1];

‌} ngx_http_limit_conn_node_t;

typedef struct {

    ngx_shm_zone_t            *shm_zone;

    ngx_rbtree_node_t         *node;

‌} ngx_http_limit_conn_cleanup_t;

typedef struct {

    ngx_rbtree_t              *rbtree;

    ngx_http_complex_value_t   key;

‌} ngx_http_limit_conn_ctx_t;

typedef struct {

    ngx_shm_zone_t            *shm_zone;

    ngx_uint_t                 conn;

‌} ngx_http_limit_conn_limit_t;

typedef struct {

    ngx_array_t                limits;

    ngx_uint_t                 log_level;

    ngx_uint_t                 status_code;

‌} ngx_http_limit_conn_conf_t;

static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,

    ngx_str_t *key, uint32_t hash);

static void ngx_http_limit_conn_cleanup(void *data);

static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);

static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);

static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);

‌static ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {

    { ngx_string("info"), NGX_LOG_INFO },

    { ngx_string("notice"), NGX_LOG_NOTICE },

    { ngx_string("warn"), NGX_LOG_WARN },

    { ngx_string("error"), NGX_LOG_ERR },

    { ngx_null_string, 0 }

};

‌static ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {

    ngx_conf_check_num_bounds, 400, 599

};

‌static ngx_command_t  ngx_http_limit_conn_commands[] = {

    { ngx_string("limit_conn_zone"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,

      ngx_http_limit_conn_zone,

      0,

      0,

      NULL },

    { ngx_string("limit_conn"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_http_limit_conn,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("limit_conn_log_level"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_limit_conn_conf_t, log_level),

      &ngx_http_limit_conn_log_levels },

    { ngx_string("limit_conn_status"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_limit_conn_conf_t, status_code),

      &ngx_http_limit_conn_status_bounds },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_limit_conn_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_limit_conn_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_limit_conn_create_conf,       /* create location configuration */

    ngx_http_limit_conn_merge_conf         /* merge location configuration */

};

‌ngx_module_t  ngx_http_limit_conn_module = {

    NGX_MODULE_V1,

    &ngx_http_limit_conn_module_ctx,       /* module context */

    ngx_http_limit_conn_commands,          /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_limit_conn_handler(ngx_http_request_t *r)

{

    size_t                          n;

    uint32_t                        hash;

    ngx_str_t                       key;

    ngx_uint_t                      i;

    ngx_slab_pool_t                *shpool;

    ngx_rbtree_node_t              *node;

    ngx_pool_cleanup_t             *cln;

    ngx_http_limit_conn_ctx_t      *ctx;

    ngx_http_limit_conn_node_t     *lc;

    ngx_http_limit_conn_conf_t     *lccf;

    ngx_http_limit_conn_limit_t    *limits;

    ngx_http_limit_conn_cleanup_t  *lccln;

    if (r->main->limit_conn_set) {

        return NGX_DECLINED;

    }

    lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);

    limits = lccf->limits.elts;

    for (i = 0; i < lccf->limits.nelts; i++) {

        ctx = limits[i].shm_zone->data;

        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        if (key.len == 0) {

            continue;

        }

        if (key.len > 255) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "the value of the \"%V\" key "

                          "is more than 255 bytes: \"%V\"",

                          &ctx->key.value, &key);

            continue;

        }

        r->main->limit_conn_set = 1;

        hash = ngx_crc32_short(key.data, key.len);

        shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;

        ngx_shmtx_lock(&shpool->mutex);

        node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash);

        if (node == NULL) {

            n = offsetof(ngx_rbtree_node_t, color)

                + offsetof(ngx_http_limit_conn_node_t, data)

                + key.len;

            node = ngx_slab_alloc_locked(shpool, n);

            if (node == NULL) {

                ngx_shmtx_unlock(&shpool->mutex);

                ngx_http_limit_conn_cleanup_all(r->pool);

                return lccf->status_code;

            }

            lc = (ngx_http_limit_conn_node_t *) &node->color;

            node->key = hash;

            lc->len = (u_char) key.len;

            lc->conn = 1;

            ngx_memcpy(lc->data, key.data, key.len);

            ngx_rbtree_insert(ctx->rbtree, node);

        } else {

            lc = (ngx_http_limit_conn_node_t *) &node->color;

            if ((ngx_uint_t) lc->conn >= limits[i].conn) {

                ngx_shmtx_unlock(&shpool->mutex);

                ngx_log_error(lccf->log_level, r->connection->log, 0,

                              "limiting connections by zone \"%V\"",

                              &limits[i].shm_zone->shm.name);

                ngx_http_limit_conn_cleanup_all(r->pool);

                return lccf->status_code;

            }

            lc->conn++;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "limit conn: %08XD %d", node->key, lc->conn);

        ngx_shmtx_unlock(&shpool->mutex);

        cln = ngx_pool_cleanup_add(r->pool,

                                   sizeof(ngx_http_limit_conn_cleanup_t));

        if (cln == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        cln->handler = ngx_http_limit_conn_cleanup;

        lccln = cln->data;

        lccln->shm_zone = limits[i].shm_zone;

        lccln->node = node;

    }

    return NGX_DECLINED;

}

static void

‌ngx_http_limit_conn_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_limit_conn_node_t   *lcn, *lcnt;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            lcn = (ngx_http_limit_conn_node_t *) &node->color;

            lcnt = (ngx_http_limit_conn_node_t *) &temp->color;

            p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)

                ? &temp->left : &temp->right;

        }

        if (*p == sentinel) {

            break;

        }

        temp = *p;

    }

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);

}

static ngx_rbtree_node_t *

‌ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)

{

    ngx_int_t                    rc;

    ngx_rbtree_node_t           *node, *sentinel;

    ngx_http_limit_conn_node_t  *lcn;

    node = rbtree->root;

    sentinel = rbtree->sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        lcn = (ngx_http_limit_conn_node_t *) &node->color;

        rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);

        if (rc == 0) {

            return node;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    return NULL;

}

static void

‌ngx_http_limit_conn_cleanup(void *data)

{

    ngx_http_limit_conn_cleanup_t  *lccln = data;

    ngx_slab_pool_t             *shpool;

    ngx_rbtree_node_t           *node;

    ngx_http_limit_conn_ctx_t   *ctx;

    ngx_http_limit_conn_node_t  *lc;

    ctx = lccln->shm_zone->data;

    shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;

    node = lccln->node;

    lc = (ngx_http_limit_conn_node_t *) &node->color;

    ngx_shmtx_lock(&shpool->mutex);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,

                   "limit conn cleanup: %08XD %d", node->key, lc->conn);

    lc->conn--;

    if (lc->conn == 0) {

        ngx_rbtree_delete(ctx->rbtree, node);

        ngx_slab_free_locked(shpool, node);

    }

    ngx_shmtx_unlock(&shpool->mutex);

}

static ngx_inline void

‌ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)

{

    ngx_pool_cleanup_t  *cln;

    cln = pool->cleanup;

    while (cln && cln->handler == ngx_http_limit_conn_cleanup) {

        ngx_http_limit_conn_cleanup(cln->data);

        cln = cln->next;

    }

    pool->cleanup = cln;

}

static ngx_int_t

‌ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)

{

    ngx_http_limit_conn_ctx_t  *octx = data;

    size_t                      len;

    ngx_slab_pool_t            *shpool;

    ngx_rbtree_node_t          *sentinel;

    ngx_http_limit_conn_ctx_t  *ctx;

    ctx = shm_zone->data;

    if (octx) {

        if (ctx->key.value.len != octx->key.value.len

            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,

                           ctx->key.value.len)

               != 0)

        {

            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,

                          "limit_conn_zone \"%V\" uses the \"%V\" key "

                          "while previously it used the \"%V\" key",

                          &shm_zone->shm.name, &ctx->key.value,

                          &octx->key.value);

            return NGX_ERROR;

        }

        ctx->rbtree = octx->rbtree;

        return NGX_OK;

    }

    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    if (shm_zone->shm.exists) {

        ctx->rbtree = shpool->data;

        return NGX_OK;

    }

    ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));

    if (ctx->rbtree == NULL) {

        return NGX_ERROR;

    }

    shpool->data = ctx->rbtree;

    sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));

    if (sentinel == NULL) {

        return NGX_ERROR;

    }

    ngx_rbtree_init(ctx->rbtree, sentinel,

                    ngx_http_limit_conn_rbtree_insert_value);

    len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;

    shpool->log_ctx = ngx_slab_alloc(shpool, len);

    if (shpool->log_ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",

                &shm_zone->shm.name);

    return NGX_OK;

}

static void *

‌ngx_http_limit_conn_create_conf(ngx_conf_t *cf)

{

    ngx_http_limit_conn_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->limits.elts = NULL;

     */

    conf->log_level = NGX_CONF_UNSET_UINT;

    conf->status_code = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_limit_conn_conf_t *prev = parent;

    ngx_http_limit_conn_conf_t *conf = child;

    if (conf->limits.elts == NULL) {

        conf->limits = prev->limits;

    }

    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);

    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,

                              NGX_HTTP_SERVICE_UNAVAILABLE);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    u_char                            *p;

    ssize_t                            size;

    ngx_str_t                         *value, name, s;

    ngx_uint_t                         i;

    ngx_shm_zone_t                    *shm_zone;

    ngx_http_limit_conn_ctx_t         *ctx;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &ctx->key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    size = 0;

    name.len = 0;

    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {

            name.data = value[i].data + 5;

            p = (u_char *) ngx_strchr(name.data, ':');

            if (p == NULL) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid zone size \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            name.len = p - name.data;

            s.data = p + 1;

            s.len = value[i].data + value[i].len - s.data;

            size = ngx_parse_size(&s);

            if (size == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid zone size \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            if (size < (ssize_t) (8 * ngx_pagesize)) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "zone \"%V\" is too small", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (name.len == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%V\" must have \"zone\" parameter",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    shm_zone = ngx_shared_memory_add(cf, &name, size,

                                     &ngx_http_limit_conn_module);

    if (shm_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    if (shm_zone->data) {

        ctx = shm_zone->data;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "%V \"%V\" is already bound to key \"%V\"",

                           &cmd->name, &name, &ctx->key.value);

        return NGX_CONF_ERROR;

    }

    shm_zone->init = ngx_http_limit_conn_init_zone;

    shm_zone->data = ctx;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_shm_zone_t               *shm_zone;

    ngx_http_limit_conn_conf_t   *lccf = conf;

    ngx_http_limit_conn_limit_t  *limit, *limits;

    ngx_str_t  *value;

    ngx_int_t   n;

    ngx_uint_t  i;

    value = cf->args->elts;

    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,

                                     &ngx_http_limit_conn_module);

    if (shm_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    limits = lccf->limits.elts;

    if (limits == NULL) {

        if (ngx_array_init(&lccf->limits, cf->pool, 1,

                           sizeof(ngx_http_limit_conn_limit_t))

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    for (i = 0; i < lccf->limits.nelts; i++) {

        if (shm_zone == limits[i].shm_zone) {

            return "is duplicate";

        }

    }

    n = ngx_atoi(value[2].data, value[2].len);

    if (n <= 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid number of connections \"%V\"", &value[2]);

        return NGX_CONF_ERROR;

    }

    if (n > 65535) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "connection limit must be less 65536");

        return NGX_CONF_ERROR;

    }

    limit = ngx_array_push(&lccf->limits);

    if (limit == NULL) {

        return NGX_CONF_ERROR;

    }

    limit->conn = n;

    limit->shm_zone = shm_zone;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_limit_conn_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_PREACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_limit_conn_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_limit_req_module.c - nginx-1.7.9

Global variables defined

ngx_http_limit_req_commands

ngx_http_limit_req_log_levels

ngx_http_limit_req_module

ngx_http_limit_req_module_ctx

ngx_http_limit_req_status_bounds

Data types defined

ngx_http_limit_req_conf_t

ngx_http_limit_req_ctx_t

ngx_http_limit_req_limit_t

ngx_http_limit_req_node_t

ngx_http_limit_req_shctx_t

Functions defined

ngx_http_limit_req

ngx_http_limit_req_account

ngx_http_limit_req_create_conf

ngx_http_limit_req_delay

ngx_http_limit_req_expire

ngx_http_limit_req_handler

ngx_http_limit_req_init

ngx_http_limit_req_init_zone

ngx_http_limit_req_lookup

ngx_http_limit_req_merge_conf

ngx_http_limit_req_rbtree_insert_value

ngx_http_limit_req_zone

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    u_char                       color;

    u_char                       dummy;

    u_short                      len;

    ngx_queue_t                  queue;

    ngx_msec_t                   last;

    /* integer value, 1 corresponds to 0.001 r/s */

    ngx_uint_t                   excess;

    ngx_uint_t                   count;

    u_char                       data[1];

‌} ngx_http_limit_req_node_t;

typedef struct {

    ngx_rbtree_t                  rbtree;

    ngx_rbtree_node_t             sentinel;

    ngx_queue_t                   queue;

‌} ngx_http_limit_req_shctx_t;

typedef struct {

    ngx_http_limit_req_shctx_t  *sh;

    ngx_slab_pool_t             *shpool;

    /* integer value, 1 corresponds to 0.001 r/s */

    ngx_uint_t                   rate;

    ngx_http_complex_value_t     key;

    ngx_http_limit_req_node_t   *node;

‌} ngx_http_limit_req_ctx_t;

typedef struct {

    ngx_shm_zone_t              *shm_zone;

    /* integer value, 1 corresponds to 0.001 r/s */

    ngx_uint_t                   burst;

    ngx_uint_t                   nodelay; /* unsigned  nodelay:1 */

‌} ngx_http_limit_req_limit_t;

typedef struct {

    ngx_array_t                  limits;

    ngx_uint_t                   limit_log_level;

    ngx_uint_t                   delay_log_level;

    ngx_uint_t                   status_code;

‌} ngx_http_limit_req_conf_t;

static void ngx_http_limit_req_delay(ngx_http_request_t *r);

static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,

    ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);

static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,

    ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);

static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,

    ngx_uint_t n);

static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);

static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);

‌static ngx_conf_enum_t  ngx_http_limit_req_log_levels[] = {

    { ngx_string("info"), NGX_LOG_INFO },

    { ngx_string("notice"), NGX_LOG_NOTICE },

    { ngx_string("warn"), NGX_LOG_WARN },

    { ngx_string("error"), NGX_LOG_ERR },

    { ngx_null_string, 0 }

};

‌static ngx_conf_num_bounds_t  ngx_http_limit_req_status_bounds = {

    ngx_conf_check_num_bounds, 400, 599

};

‌static ngx_command_t  ngx_http_limit_req_commands[] = {

    { ngx_string("limit_req_zone"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,

      ngx_http_limit_req_zone,

      0,

      0,

      NULL },

    { ngx_string("limit_req"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_http_limit_req,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("limit_req_log_level"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_limit_req_conf_t, limit_log_level),

      &ngx_http_limit_req_log_levels },

    { ngx_string("limit_req_status"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_limit_req_conf_t, status_code),

      &ngx_http_limit_req_status_bounds },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_limit_req_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_limit_req_init,               /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_limit_req_create_conf,        /* create location configuration */

    ngx_http_limit_req_merge_conf          /* merge location configuration */

};

‌ngx_module_t  ngx_http_limit_req_module = {

    NGX_MODULE_V1,

    &ngx_http_limit_req_module_ctx,        /* module context */

    ngx_http_limit_req_commands,           /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_limit_req_handler(ngx_http_request_t *r)

{

    uint32_t                     hash;

    ngx_str_t                    key;

    ngx_int_t                    rc;

    ngx_uint_t                   n, excess;

    ngx_msec_t                   delay;

    ngx_http_limit_req_ctx_t    *ctx;

    ngx_http_limit_req_conf_t   *lrcf;

    ngx_http_limit_req_limit_t  *limit, *limits;

    if (r->main->limit_req_set) {

        return NGX_DECLINED;

    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);

    limits = lrcf->limits.elts;

    excess = 0;

    rc = NGX_DECLINED;

#if (NGX_SUPPRESS_WARN)

    limit = NULL;

#endif

    for (n = 0; n < lrcf->limits.nelts; n++) {

        limit = &limits[n];

        ctx = limit->shm_zone->data;

        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        if (key.len == 0) {

            continue;

        }

        if (key.len > 65535) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "the value of the \"%V\" key "

                          "is more than 65535 bytes: \"%V\"",

                          &ctx->key.value, &key);

            continue;

        }

        hash = ngx_crc32_short(key.data, key.len);

        ngx_shmtx_lock(&ctx->shpool->mutex);

        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,

                                       (n == lrcf->limits.nelts - 1));

        ngx_shmtx_unlock(&ctx->shpool->mutex);

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "limit_req[%ui]: %i %ui.%03ui",

                       n, rc, excess / 1000, excess % 1000);

        if (rc != NGX_AGAIN) {

            break;

        }

    }

    if (rc == NGX_DECLINED) {

        return NGX_DECLINED;

    }

    r->main->limit_req_set = 1;

    if (rc == NGX_BUSY || rc == NGX_ERROR) {

        if (rc == NGX_BUSY) {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,

                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",

                          excess / 1000, excess % 1000,

                          &limit->shm_zone->shm.name);

        }

        while (n--) {

            ctx = limits[n].shm_zone->data;

            if (ctx->node == NULL) {

                continue;

            }

            ngx_shmtx_lock(&ctx->shpool->mutex);

            ctx->node->count--;

            ngx_shmtx_unlock(&ctx->shpool->mutex);

            ctx->node = NULL;

        }

        return lrcf->status_code;

    }

    /* rc == NGX_AGAIN || rc == NGX_OK */

    if (rc == NGX_AGAIN) {

        excess = 0;

    }

    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);

    if (!delay) {

        return NGX_DECLINED;

    }

    ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,

                  "delaying request, excess: %ui.%03ui, by zone \"%V\"",

                  excess / 1000, excess % 1000, &limit->shm_zone->shm.name);

    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    r->read_event_handler = ngx_http_test_reading;

    r->write_event_handler = ngx_http_limit_req_delay;

    ngx_add_timer(r->connection->write, delay);

    return NGX_AGAIN;

}

static void

‌ngx_http_limit_req_delay(ngx_http_request_t *r)

{

    ngx_event_t  *wev;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "limit_req delay");

    wev = r->connection->write;

    if (!wev->timedout) {

        if (ngx_handle_write_event(wev, 0) != NGX_OK) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        }

        return;

    }

    wev->timedout = 0;

    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    r->read_event_handler = ngx_http_block_reading;

    r->write_event_handler = ngx_http_core_run_phases;

    ngx_http_core_run_phases(r);

}

static void

‌ngx_http_limit_req_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_limit_req_node_t   *lrn, *lrnt;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            lrn = (ngx_http_limit_req_node_t *) &node->color;

            lrnt = (ngx_http_limit_req_node_t *) &temp->color;

            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)

                ? &temp->left : &temp->right;

        }

        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_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,

    ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)

{

    size_t                      size;

    ngx_int_t                   rc, excess;

    ngx_time_t                 *tp;

    ngx_msec_t                  now;

    ngx_msec_int_t              ms;

    ngx_rbtree_node_t          *node, *sentinel;

    ngx_http_limit_req_ctx_t   *ctx;

    ngx_http_limit_req_node_t  *lr;

    tp = ngx_timeofday();

    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

    ctx = limit->shm_zone->data;

    node = ctx->sh->rbtree.root;

    sentinel = ctx->sh->rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {

            node = node->left;

            continue;

        }

        if (hash > node->key) {

            node = node->right;

            continue;

        }

        /* hash == node->key */

        lr = (ngx_http_limit_req_node_t *) &node->color;

        rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);

        if (rc == 0) {

            ngx_queue_remove(&lr->queue);

            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

            ms = (ngx_msec_int_t) (now - lr->last);

            excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;

            if (excess < 0) {

                excess = 0;

            }

            *ep = excess;

            if ((ngx_uint_t) excess > limit->burst) {

                return NGX_BUSY;

            }

            if (account) {

                lr->excess = excess;

                lr->last = now;

                return NGX_OK;

            }

            lr->count++;

            ctx->node = lr;

            return NGX_AGAIN;

        }

        node = (rc < 0) ? node->left : node->right;

    }

    *ep = 0;

    size = offsetof(ngx_rbtree_node_t, color)

           + offsetof(ngx_http_limit_req_node_t, data)

           + key->len;

    ngx_http_limit_req_expire(ctx, 1);

    node = ngx_slab_alloc_locked(ctx->shpool, size);

    if (node == NULL) {

        ngx_http_limit_req_expire(ctx, 0);

        node = ngx_slab_alloc_locked(ctx->shpool, size);

        if (node == NULL) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,

                          "could not allocate node%s", ctx->shpool->log_ctx);

            return NGX_ERROR;

        }

    }

    node->key = hash;

    lr = (ngx_http_limit_req_node_t *) &node->color;

    lr->len = (u_short) key->len;

    lr->excess = 0;

    ngx_memcpy(lr->data, key->data, key->len);

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

    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

    if (account) {

        lr->last = now;

        lr->count = 0;

        return NGX_OK;

    }

    lr->last = 0;

    lr->count = 1;

    ctx->node = lr;

    return NGX_AGAIN;

}

static ngx_msec_t

‌ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,

    ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)

{

    ngx_int_t                   excess;

    ngx_time_t                 *tp;

    ngx_msec_t                  now, delay, max_delay;

    ngx_msec_int_t              ms;

    ngx_http_limit_req_ctx_t   *ctx;

    ngx_http_limit_req_node_t  *lr;

    excess = *ep;

    if (excess == 0 || (*limit)->nodelay) {

        max_delay = 0;

    } else {

        ctx = (*limit)->shm_zone->data;

        max_delay = excess * 1000 / ctx->rate;

    }

    while (n--) {

        ctx = limits[n].shm_zone->data;

        lr = ctx->node;

        if (lr == NULL) {

            continue;

        }

        ngx_shmtx_lock(&ctx->shpool->mutex);

        tp = ngx_timeofday();

        now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

        ms = (ngx_msec_int_t) (now - lr->last);

        excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;

        if (excess < 0) {

            excess = 0;

        }

        lr->last = now;

        lr->excess = excess;

        lr->count--;

        ngx_shmtx_unlock(&ctx->shpool->mutex);

        ctx->node = NULL;

        if (limits[n].nodelay) {

            continue;

        }

        delay = excess * 1000 / ctx->rate;

        if (delay > max_delay) {

            max_delay = delay;

            *ep = excess;

            *limit = &limits[n];

        }

    }

    return max_delay;

}

static void

‌ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)

{

    ngx_int_t                   excess;

    ngx_time_t                 *tp;

    ngx_msec_t                  now;

    ngx_queue_t                *q;

    ngx_msec_int_t              ms;

    ngx_rbtree_node_t          *node;

    ngx_http_limit_req_node_t  *lr;

    tp = ngx_timeofday();

    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

    /*

     * n == 1 deletes one or two zero rate entries

     * n == 0 deletes oldest entry by force

     *        and one or two zero rate entries

     */

    while (n < 3) {

        if (ngx_queue_empty(&ctx->sh->queue)) {

            return;

        }

        q = ngx_queue_last(&ctx->sh->queue);

        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);

        if (lr->count) {

            /*

             * There is not much sense in looking further,

             * because we bump nodes on the lookup stage.

             */

            return;

        }

        if (n++ != 0) {

            ms = (ngx_msec_int_t) (now - lr->last);

            ms = ngx_abs(ms);

            if (ms < 60000) {

                return;

            }

            excess = lr->excess - ctx->rate * ms / 1000;

            if (excess > 0) {

                return;

            }

        }

        ngx_queue_remove(q);

        node = (ngx_rbtree_node_t *)

                   ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));

        ngx_rbtree_delete(&ctx->sh->rbtree, node);

        ngx_slab_free_locked(ctx->shpool, node);

    }

}

static ngx_int_t

‌ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)

{

    ngx_http_limit_req_ctx_t  *octx = data;

    size_t                     len;

    ngx_http_limit_req_ctx_t  *ctx;

    ctx = shm_zone->data;

    if (octx) {

        if (ctx->key.value.len != octx->key.value.len

            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,

                           ctx->key.value.len)

               != 0)

        {

            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,

                          "limit_req \"%V\" uses the \"%V\" key "

                          "while previously it used the \"%V\" key",

                          &shm_zone->shm.name, &ctx->key.value,

                          &octx->key.value);

            return NGX_ERROR;

        }

        ctx->sh = octx->sh;

        ctx->shpool = octx->shpool;

        return NGX_OK;

    }

    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    if (shm_zone->shm.exists) {

        ctx->sh = ctx->shpool->data;

        return NGX_OK;

    }

    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));

    if (ctx->sh == NULL) {

        return NGX_ERROR;

    }

    ctx->shpool->data = ctx->sh;

    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,

                    ngx_http_limit_req_rbtree_insert_value);

    ngx_queue_init(&ctx->sh->queue);

    len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;

    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);

    if (ctx->shpool->log_ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",

                &shm_zone->shm.name);

    ctx->shpool->log_nomem = 0;

    return NGX_OK;

}

static void *

‌ngx_http_limit_req_create_conf(ngx_conf_t *cf)

{

    ngx_http_limit_req_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->limits.elts = NULL;

     */

    conf->limit_log_level = NGX_CONF_UNSET_UINT;

    conf->status_code = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_limit_req_conf_t *prev = parent;

    ngx_http_limit_req_conf_t *conf = child;

    if (conf->limits.elts == NULL) {

        conf->limits = prev->limits;

    }

    ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,

                              NGX_LOG_ERR);

    conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?

                                NGX_LOG_INFO : conf->limit_log_level + 1;

    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,

                              NGX_HTTP_SERVICE_UNAVAILABLE);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    u_char                            *p;

    size_t                             len;

    ssize_t                            size;

    ngx_str_t                         *value, name, s;

    ngx_int_t                          rate, scale;

    ngx_uint_t                         i;

    ngx_shm_zone_t                    *shm_zone;

    ngx_http_limit_req_ctx_t          *ctx;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &ctx->key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    size = 0;

    rate = 1;

    scale = 1;

    name.len = 0;

    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {

            name.data = value[i].data + 5;

            p = (u_char *) ngx_strchr(name.data, ':');

            if (p == NULL) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid zone size \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            name.len = p - name.data;

            s.data = p + 1;

            s.len = value[i].data + value[i].len - s.data;

            size = ngx_parse_size(&s);

            if (size == NGX_ERROR) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid zone size \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            if (size < (ssize_t) (8 * ngx_pagesize)) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "zone \"%V\" is too small", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {

            len = value[i].len;

            p = value[i].data + len - 3;

            if (ngx_strncmp(p, "r/s", 3) == 0) {

                scale = 1;

                len -= 3;

            } else if (ngx_strncmp(p, "r/m", 3) == 0) {

                scale = 60;

                len -= 3;

            }

            rate = ngx_atoi(value[i].data + 5, len - 5);

            if (rate <= 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid rate \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (name.len == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%V\" must have \"zone\" parameter",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    ctx->rate = rate * 1000 / scale;

    shm_zone = ngx_shared_memory_add(cf, &name, size,

                                     &ngx_http_limit_req_module);

    if (shm_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    if (shm_zone->data) {

        ctx = shm_zone->data;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "%V \"%V\" is already bound to key \"%V\"",

                           &cmd->name, &name, &ctx->key.value);

        return NGX_CONF_ERROR;

    }

    shm_zone->init = ngx_http_limit_req_init_zone;

    shm_zone->data = ctx;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_limit_req_conf_t  *lrcf = conf;

    ngx_int_t                    burst;

    ngx_str_t                   *value, s;

    ngx_uint_t                   i, nodelay;

    ngx_shm_zone_t              *shm_zone;

    ngx_http_limit_req_limit_t  *limit, *limits;

    value = cf->args->elts;

    shm_zone = NULL;

    burst = 0;

    nodelay = 0;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {

            s.len = value[i].len - 5;

            s.data = value[i].data + 5;

            shm_zone = ngx_shared_memory_add(cf, &s, 0,

                                             &ngx_http_limit_req_module);

            if (shm_zone == NULL) {

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {

            burst = ngx_atoi(value[i].data + 6, value[i].len - 6);

            if (burst <= 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid burst rate \"%V\"", &value[i]);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strcmp(value[i].data, "nodelay") == 0) {

            nodelay = 1;

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (shm_zone == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"%V\" must have \"zone\" parameter",

                           &cmd->name);

        return NGX_CONF_ERROR;

    }

    if (shm_zone->data == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "unknown limit_req_zone \"%V\"",

                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;

    }

    limits = lrcf->limits.elts;

    if (limits == NULL) {

        if (ngx_array_init(&lrcf->limits, cf->pool, 1,

                           sizeof(ngx_http_limit_req_limit_t))

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    for (i = 0; i < lrcf->limits.nelts; i++) {

        if (shm_zone == limits[i].shm_zone) {

            return "is duplicate";

        }

    }

    limit = ngx_array_push(&lrcf->limits);

    if (limit == NULL) {

        return NGX_CONF_ERROR;

    }

    limit->shm_zone = shm_zone;

    limit->burst = burst * 1000;

    limit->nodelay = nodelay;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_limit_req_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_PREACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_limit_req_handler;

    return NGX_OK;

}

src/http/ngx_http_busy_lock.c - nginx-1.7.9

Functions defined

ngx_http_busy_lock

ngx_http_busy_lock_cacheable

ngx_http_busy_lock_look_cacheable

ngx_http_busy_unlock

ngx_http_set_busy_lock_slot

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,

                                             ngx_http_busy_lock_ctx_t *bc,

                                             int lock);

‌int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)

{

    if (bl->busy < bl->max_busy) {

        bl->busy++;

        if (bc->time) {

            bc->time = 0;

            bl->waiting--;

        }

        return NGX_OK;

    }

    if (bc->time) {

        if (bc->time < bl->timeout) {

            ngx_add_timer(bc->event, 1000);

            return NGX_AGAIN;

        }

        bl->waiting--;

        return NGX_DONE;

    }

    if (bl->timeout == 0) {

        return NGX_DONE;

    }

    if (bl->waiting < bl->max_waiting) {

        bl->waiting++;

#if 0

        ngx_add_timer(bc->event, 1000);

        bc->event->event_handler = bc->event_handler;

#endif

        /* TODO: ngx_handle_level_read_event() */

        return NGX_AGAIN;

    }

    return NGX_ERROR;

}

‌int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,

                                 ngx_http_busy_lock_ctx_t *bc, int lock)

{

    int  rc;

    rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0,

                   "http busylock: %d w:%d mw::%d",

                   rc, bl->waiting, bl->max_waiting);

    if (rc == NGX_OK) {  /* no the same request, there's free slot */

        return NGX_OK;

    }

    if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */

        return NGX_OK;

    }

    /* rc == NGX_AGAIN:  the same request */

    if (bc->time) {

        if (bc->time < bl->timeout) {

            ngx_add_timer(bc->event, 1000);

            return NGX_AGAIN;

        }

        bl->waiting--;

        return NGX_DONE;

    }

    if (bl->timeout == 0) {

        return NGX_DONE;

    }

    if (bl->waiting < bl->max_waiting) {

#if 0

        bl->waiting++;

        ngx_add_timer(bc->event, 1000);

        bc->event->event_handler = bc->event_handler;

#endif

        /* TODO: ngx_handle_level_read_event() */

        return NGX_AGAIN;

    }

    return NGX_ERROR;

}

‌void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,

                          ngx_http_busy_lock_ctx_t *bc)

{

    if (bl == NULL) {

        return;

    }

    if (bl->md5) {

        bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));

        bl->cacheable--;

    }

    bl->busy--;

}

‌static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,

                                             ngx_http_busy_lock_ctx_t *bc,

                                             int lock)

{

    int    i, b, cacheable, free;

    u_int  mask;

    b = 0;

    cacheable = 0;

    free = -1;

#if (NGX_SUPPRESS_WARN)

    mask = 0;

#endif

    for (i = 0; i < bl->max_busy; i++) {

        if ((b & 7) == 0) {

            mask = bl->md5_mask[i / 8];

        }

        if (mask & 1) {

            if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {

                return NGX_AGAIN;

            }

            cacheable++;

        } else if (free == -1) {

            free = i;

        }

#if 1

        if (cacheable == bl->cacheable) {

            if (free == -1 && cacheable < bl->max_busy) {

                free = i + 1;

            }

            break;

        }

#endif

        mask >>= 1;

        b++;

    }

    if (free == -1) {

        return NGX_ERROR;

    }

    if (lock) {

        if (bl->busy == bl->max_busy) {

            return NGX_ERROR;

        }

        ngx_memcpy(&bl->md5[free * 16], bc->md5, 16);

        bl->md5_mask[free / 8] |= 1 << (free & 7);

        bc->slot = free;

        bl->cacheable++;

        bl->busy++;

    }

    return NGX_OK;

}

‌char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,

                                  void *conf)

{

    char  *p = conf;

    ngx_uint_t             i, dup, invalid;

    ngx_str_t             *value, line;

    ngx_http_busy_lock_t  *bl, **blp;

    blp = (ngx_http_busy_lock_t **) (p + cmd->offset);

    if (*blp) {

        return "is duplicate";

    }

    /* ngx_calloc_shared() */

    bl = ngx_pcalloc(cf->pool, sizeof(ngx_http_busy_lock_t));

    if (bl == NULL) {

        return NGX_CONF_ERROR;

    }

    *blp = bl;

    /* ngx_calloc_shared() */

    bl->mutex = ngx_pcalloc(cf->pool, sizeof(ngx_event_mutex_t));

    if (bl->mutex == NULL) {

        return NGX_CONF_ERROR;

    }

    dup = 0;

    invalid = 0;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (value[i].data[1] != '=') {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid value \"%s\"", value[i].data);

            return NGX_CONF_ERROR;

        }

        switch (value[i].data[0]) {

        case 'b':

            if (bl->max_busy) {

                dup = 1;

                break;

            }

            bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2);

            if (bl->max_busy == NGX_ERROR) {

                invalid = 1;

                break;

            }

            continue;

        case 'w':

            if (bl->max_waiting) {

                dup = 1;

                break;

            }

            bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2);

            if (bl->max_waiting == NGX_ERROR) {

                invalid = 1;

                break;

            }

            continue;

        case 't':

            if (bl->timeout) {

                dup = 1;

                break;

            }

            line.len = value[i].len - 2;

            line.data = value[i].data + 2;

            bl->timeout = ngx_parse_time(&line, 1);

            if (bl->timeout == (time_t) NGX_ERROR) {

                invalid = 1;

                break;

            }

            continue;

        default:

            invalid = 1;

        }

        if (dup) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "duplicate value \"%s\"", value[i].data);

            return NGX_CONF_ERROR;

        }

        if (invalid) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%s\"", value[i].data);

            return NGX_CONF_ERROR;

        }

    }

    if (bl->timeout == 0 && bl->max_waiting) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                   "busy lock waiting is useless with zero timeout, ignoring");

    }

    return NGX_CONF_OK;

}

src/http/ngx_http_busy_lock.h - nginx-1.7.9

Data types defined

ngx_http_busy_lock_ctx_t

ngx_http_busy_lock_t

Macros defined

_NGX_HTTP_BUSY_LOCK_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_BUSY_LOCK_H_INCLUDED_

‌#define _NGX_HTTP_BUSY_LOCK_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#include <ngx_http.h>

typedef struct {

    u_char             *md5_mask;

    char               *md5;

    int                 cacheable;

    int                 busy;

    int                 max_busy;

    int                 waiting;

    int                 max_waiting;

    time_t              timeout;

    ngx_event_mutex_t  *mutex;

‌} ngx_http_busy_lock_t;

typedef struct {

    time_t         time;

    ngx_event_t   *event;

    void         (*event_handler)(ngx_event_t *ev);

    u_char        *md5;

    int            slot;

‌} ngx_http_busy_lock_ctx_t;

int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc);

int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,

                                 ngx_http_busy_lock_ctx_t *bc, int lock);

void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,

                          ngx_http_busy_lock_ctx_t *bc);

char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,

                                  void *conf);

#endif /* _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ */

src/http/modules/ngx_http_ssi_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_body_filter

ngx_http_next_header_filter

ngx_http_ssi_block_params

ngx_http_ssi_commands

ngx_http_ssi_config_params

ngx_http_ssi_echo_params

ngx_http_ssi_filter_commands

ngx_http_ssi_filter_module

ngx_http_ssi_filter_module_ctx

ngx_http_ssi_if_params

ngx_http_ssi_include_params

ngx_http_ssi_no_params

ngx_http_ssi_none

ngx_http_ssi_null_string

ngx_http_ssi_set_params

ngx_http_ssi_string

ngx_http_ssi_timefmt

ngx_http_ssi_vars

Data types defined

ngx_http_ssi_block_t

ngx_http_ssi_loc_conf_t

ngx_http_ssi_state_e

ngx_http_ssi_var_t

Functions defined

ngx_http_ssi_block

ngx_http_ssi_body_filter

ngx_http_ssi_buffered

ngx_http_ssi_config

ngx_http_ssi_create_loc_conf

ngx_http_ssi_create_main_conf

ngx_http_ssi_date_gmt_local_variable

ngx_http_ssi_echo

ngx_http_ssi_else

ngx_http_ssi_endblock

ngx_http_ssi_endif

ngx_http_ssi_evaluate_string

ngx_http_ssi_filter_init

ngx_http_ssi_get_variable

ngx_http_ssi_header_filter

ngx_http_ssi_if

ngx_http_ssi_include

ngx_http_ssi_init_main_conf

ngx_http_ssi_merge_loc_conf

ngx_http_ssi_output

ngx_http_ssi_parse

ngx_http_ssi_preconfiguration

ngx_http_ssi_regex_match

ngx_http_ssi_set

ngx_http_ssi_set_variable

ngx_http_ssi_stub_output

Macros defined

NGX_HTTP_SSI_ADD_PREFIX

NGX_HTTP_SSI_ADD_ZERO

NGX_HTTP_SSI_BLOCK_NAME

NGX_HTTP_SSI_CONFIG_ERRMSG

NGX_HTTP_SSI_CONFIG_TIMEFMT

NGX_HTTP_SSI_DATE_LEN

NGX_HTTP_SSI_ECHO_DEFAULT

NGX_HTTP_SSI_ECHO_ENCODING

NGX_HTTP_SSI_ECHO_VAR

NGX_HTTP_SSI_ERROR

NGX_HTTP_SSI_IF_EXPR

NGX_HTTP_SSI_INCLUDE_FILE

NGX_HTTP_SSI_INCLUDE_SET

NGX_HTTP_SSI_INCLUDE_STUB

NGX_HTTP_SSI_INCLUDE_VIRTUAL

NGX_HTTP_SSI_INCLUDE_WAIT

NGX_HTTP_SSI_SET_VALUE

NGX_HTTP_SSI_SET_VAR

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_SSI_ERROR          1

‌#define NGX_HTTP_SSI_DATE_LEN       2048

‌#define NGX_HTTP_SSI_ADD_PREFIX     1

‌#define NGX_HTTP_SSI_ADD_ZERO       2

typedef struct {

    ngx_flag_t    enable;

    ngx_flag_t    silent_errors;

    ngx_flag_t    ignore_recycled_buffers;

    ngx_flag_t    last_modified;

    ngx_hash_t    types;

    size_t        min_file_chunk;

    size_t        value_len;

    ngx_array_t  *types_keys;

‌} ngx_http_ssi_loc_conf_t;

typedef struct {

    ngx_str_t     name;

    ngx_uint_t    key;

    ngx_str_t     value;

‌} ngx_http_ssi_var_t;

typedef struct {

    ngx_str_t     name;

    ngx_chain_t  *bufs;

    ngx_uint_t    count;

‌} ngx_http_ssi_block_t;

typedef enum {

    ssi_start_state = 0,

    ssi_tag_state,

    ssi_comment0_state,

    ssi_comment1_state,

    ssi_sharp_state,

    ssi_precommand_state,

    ssi_command_state,

    ssi_preparam_state,

    ssi_param_state,

    ssi_preequal_state,

    ssi_prevalue_state,

    ssi_double_quoted_value_state,

    ssi_quoted_value_state,

    ssi_quoted_symbol_state,

    ssi_postparam_state,

    ssi_comment_end0_state,

    ssi_comment_end1_state,

    ssi_error_state,

    ssi_error_end0_state,

    ssi_error_end1_state

‌} ngx_http_ssi_state_e;

static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx);

static void ngx_http_ssi_buffered(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx);

static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx);

static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,

    ngx_str_t *name, ngx_uint_t key);

static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);

static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,

    ngx_str_t *pattern, ngx_str_t *str);

static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,

    ngx_int_t rc);

static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,

    ngx_int_t rc);

static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t gmt);

static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);

static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);

static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);

static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_ssi_filter_commands[] = {

    { ngx_string("ssi"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, enable),

      NULL },

    { ngx_string("ssi_silent_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, silent_errors),

      NULL },

    { ngx_string("ssi_ignore_recycled_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),

      NULL },

    { ngx_string("ssi_min_file_chunk"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),

      NULL },

    { ngx_string("ssi_value_length"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, value_len),

      NULL },

    { ngx_string("ssi_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, types_keys),

      &ngx_http_html_default_types[0] },

    { ngx_string("ssi_last_modified"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_ssi_loc_conf_t, last_modified),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {

    ngx_http_ssi_preconfiguration,         /* preconfiguration */

    ngx_http_ssi_filter_init,              /* postconfiguration */

    ngx_http_ssi_create_main_conf,         /* create main configuration */

    ngx_http_ssi_init_main_conf,           /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_ssi_create_loc_conf,          /* create location configuration */

    ngx_http_ssi_merge_loc_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_ssi_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_ssi_filter_module_ctx,       /* module context */

    ngx_http_ssi_filter_commands,          /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

‌static u_char ngx_http_ssi_string[] = "<!--";

‌static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");

‌static ngx_str_t ngx_http_ssi_timefmt = ngx_string("%A, %d-%b-%Y %H:%M:%S %Z");

‌static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;

‌#define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0

‌#define  NGX_HTTP_SSI_INCLUDE_FILE     1

‌#define  NGX_HTTP_SSI_INCLUDE_WAIT     2

‌#define  NGX_HTTP_SSI_INCLUDE_SET      3

‌#define  NGX_HTTP_SSI_INCLUDE_STUB     4

‌#define  NGX_HTTP_SSI_ECHO_VAR         0

‌#define  NGX_HTTP_SSI_ECHO_DEFAULT     1

‌#define  NGX_HTTP_SSI_ECHO_ENCODING    2

‌#define  NGX_HTTP_SSI_CONFIG_ERRMSG    0

‌#define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1

‌#define  NGX_HTTP_SSI_SET_VAR          0

‌#define  NGX_HTTP_SSI_SET_VALUE        1

‌#define  NGX_HTTP_SSI_IF_EXPR          0

‌#define  NGX_HTTP_SSI_BLOCK_NAME       0

‌static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {

    { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },

    { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },

    { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },

    { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },

    { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {

    { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },

    { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },

    { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {

    { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },

    { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {

    { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },

    { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {

    { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_block_params[] = {

    { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {

    { ngx_null_string, 0, 0, 0 }

};

‌static ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {

    { ngx_string("include"), ngx_http_ssi_include,

                       ngx_http_ssi_include_params, 0, 0, 1 },

    { ngx_string("echo"), ngx_http_ssi_echo,

                       ngx_http_ssi_echo_params, 0, 0, 0 },

    { ngx_string("config"), ngx_http_ssi_config,

                       ngx_http_ssi_config_params, 0, 0, 0 },

    { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },

    { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },

    { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,

                       NGX_HTTP_SSI_COND_IF, 0, 0 },

    { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,

                       NGX_HTTP_SSI_COND_IF, 0, 0 },

    { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,

                       NGX_HTTP_SSI_COND_ELSE, 0, 0 },

    { ngx_string("block"), ngx_http_ssi_block,

                       ngx_http_ssi_block_params, 0, 0, 0 },

    { ngx_string("endblock"), ngx_http_ssi_endblock,

                       ngx_http_ssi_no_params, 0, 1, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌static ngx_http_variable_t  ngx_http_ssi_vars[] = {

    { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,

      NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

static ngx_int_t

‌ngx_http_ssi_header_filter(ngx_http_request_t *r)

{

    ngx_http_ssi_ctx_t       *ctx;

    ngx_http_ssi_loc_conf_t  *slcf;

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);

    if (!slcf->enable

        || r->headers_out.content_length_n == 0

        || ngx_http_test_content_type(r, &slcf->types) == NULL)

    {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);

    ctx->value_len = slcf->value_len;

    ctx->last_out = &ctx->out;

    ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;

    ctx->output = 1;

    ctx->params.elts = ctx->params_array;

    ctx->params.size = sizeof(ngx_table_elt_t);

    ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;

    ctx->params.pool = r->pool;

    ctx->timefmt = ngx_http_ssi_timefmt;

    ngx_str_set(&ctx->errmsg,

                "[an error occurred while processing the directive]");

    r->filter_need_in_memory = 1;

    if (r == r->main) {

        ngx_http_clear_content_length(r);

        ngx_http_clear_accept_ranges(r);

        if (!slcf->last_modified) {

            ngx_http_clear_last_modified(r);

            ngx_http_clear_etag(r);

        } else {

            ngx_http_weak_etag(r);

        }

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    size_t                     len;

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_uint_t                 i, index;

    ngx_chain_t               *cl, **ll;

    ngx_table_elt_t           *param;

    ngx_http_ssi_ctx_t        *ctx, *mctx;

    ngx_http_ssi_block_t      *bl;

    ngx_http_ssi_param_t      *prm;

    ngx_http_ssi_command_t    *cmd;

    ngx_http_ssi_loc_conf_t   *slcf;

    ngx_http_ssi_main_conf_t  *smcf;

    ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];

    ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);

    if (ctx == NULL

        || (in == NULL

            && ctx->buf == NULL

            && ctx->in == NULL

            && ctx->busy == NULL))

    {

        return ngx_http_next_body_filter(r, in);

    }

    /* add the incoming chain to the chain ctx->in */

    if (in) {

        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http ssi filter \"%V?%V\"", &r->uri, &r->args);

    if (ctx->wait) {

        if (r != r->connection->data) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http ssi filter wait \"%V?%V\" non-active",

                           &ctx->wait->uri, &ctx->wait->args);

            return NGX_AGAIN;

        }

        if (ctx->wait->done) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http ssi filter wait \"%V?%V\" done",

                           &ctx->wait->uri, &ctx->wait->args);

            ctx->wait = NULL;

        } else {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http ssi filter wait \"%V?%V\"",

                           &ctx->wait->uri, &ctx->wait->args);

            return ngx_http_next_body_filter(r, NULL);

        }

    }

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);

    while (ctx->in || ctx->buf) {

        if (ctx->buf == NULL) {

            ctx->buf = ctx->in->buf;

            ctx->in = ctx->in->next;

            ctx->pos = ctx->buf->pos;

        }

        if (ctx->state == ssi_start_state) {

            ctx->copy_start = ctx->pos;

            ctx->copy_end = ctx->pos;

        }

        b = NULL;

        while (ctx->pos < ctx->buf->last) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "saved: %d state: %d", ctx->saved, ctx->state);

            rc = ngx_http_ssi_parse(r, ctx);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "parse: %d, looked: %d %p-%p",

                           rc, ctx->looked, ctx->copy_start, ctx->copy_end);

            if (rc == NGX_ERROR) {

                return rc;

            }

            if (ctx->copy_start != ctx->copy_end) {

                if (ctx->output) {

                    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                                   "saved: %d", ctx->saved);

                    if (ctx->saved) {

                        if (ctx->free) {

                            cl = ctx->free;

                            ctx->free = ctx->free->next;

                            b = cl->buf;

                            ngx_memzero(b, sizeof(ngx_buf_t));

                        } else {

                            b = ngx_calloc_buf(r->pool);

                            if (b == NULL) {

                                return NGX_ERROR;

                            }

                            cl = ngx_alloc_chain_link(r->pool);

                            if (cl == NULL) {

                                return NGX_ERROR;

                            }

                            cl->buf = b;

                        }

                        b->memory = 1;

                        b->pos = ngx_http_ssi_string;

                        b->last = ngx_http_ssi_string + ctx->saved;

                        *ctx->last_out = cl;

                        ctx->last_out = &cl->next;

                        ctx->saved = 0;

                    }

                    if (ctx->free) {

                        cl = ctx->free;

                        ctx->free = ctx->free->next;

                        b = cl->buf;

                    } else {

                        b = ngx_alloc_buf(r->pool);

                        if (b == NULL) {

                            return NGX_ERROR;

                        }

                        cl = ngx_alloc_chain_link(r->pool);

                        if (cl == NULL) {

                            return NGX_ERROR;

                        }

                        cl->buf = b;

                    }

                    ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

                    b->pos = ctx->copy_start;

                    b->last = ctx->copy_end;

                    b->shadow = NULL;

                    b->last_buf = 0;

                    b->recycled = 0;

                    if (b->in_file) {

                        if (slcf->min_file_chunk < (size_t) (b->last - b->pos))

                        {

                            b->file_last = b->file_pos

                                                   + (b->last - ctx->buf->pos);

                            b->file_pos += b->pos - ctx->buf->pos;

                        } else {

                            b->in_file = 0;

                        }

                    }

                    cl->next = NULL;

                    *ctx->last_out = cl;

                    ctx->last_out = &cl->next;

                } else {

                    if (ctx->block

                        && ctx->saved + (ctx->copy_end - ctx->copy_start))

                    {

                        b = ngx_create_temp_buf(r->pool,

                               ctx->saved + (ctx->copy_end - ctx->copy_start));

                        if (b == NULL) {

                            return NGX_ERROR;

                        }

                        if (ctx->saved) {

                            b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,

                                                 ctx->saved);

                        }

                        b->last = ngx_cpymem(b->last, ctx->copy_start,

                                             ctx->copy_end - ctx->copy_start);

                        cl = ngx_alloc_chain_link(r->pool);

                        if (cl == NULL) {

                            return NGX_ERROR;

                        }

                        cl->buf = b;

                        cl->next = NULL;

                        b = NULL;

                        mctx = ngx_http_get_module_ctx(r->main,

                                                   ngx_http_ssi_filter_module);

                        bl = mctx->blocks->elts;

                        for (ll = &bl[mctx->blocks->nelts - 1].bufs;

                             *ll;

                             ll = &(*ll)->next)

                        {

                            /* void */

                        }

                        *ll = cl;

                    }

                    ctx->saved = 0;

                }

            }

            if (ctx->state == ssi_start_state) {

                ctx->copy_start = ctx->pos;

                ctx->copy_end = ctx->pos;

            } else {

                ctx->copy_start = NULL;

                ctx->copy_end = NULL;

            }

            if (rc == NGX_AGAIN) {

                continue;

            }

            b = NULL;

            if (rc == NGX_OK) {

                smcf = ngx_http_get_module_main_conf(r,

                                                   ngx_http_ssi_filter_module);

                cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,

                                    ctx->command.len);

                if (cmd == NULL) {

                    if (ctx->output) {

                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                      "invalid SSI command: \"%V\"",

                                      &ctx->command);

                        goto ssi_error;

                    }

                    continue;

                }

                if (!ctx->output && !cmd->block) {

                    if (ctx->block) {

                        /* reconstruct the SSI command text */

                        len = 5 + ctx->command.len + 4;

                        param = ctx->params.elts;

                        for (i = 0; i < ctx->params.nelts; i++) {

                            len += 1 + param[i].key.len + 2

                                + param[i].value.len + 1;

                        }

                        b = ngx_create_temp_buf(r->pool, len);

                        if (b == NULL) {

                            return NGX_ERROR;

                        }

                        cl = ngx_alloc_chain_link(r->pool);

                        if (cl == NULL) {

                            return NGX_ERROR;

                        }

                        cl->buf = b;

                        cl->next = NULL;

                        *b->last++ = '<';

                        *b->last++ = '!';

                        *b->last++ = '-';

                        *b->last++ = '-';

                        *b->last++ = '#';

                        b->last = ngx_cpymem(b->last, ctx->command.data,

                                             ctx->command.len);

                        for (i = 0; i < ctx->params.nelts; i++) {

                            *b->last++ = ' ';

                            b->last = ngx_cpymem(b->last, param[i].key.data,

                                                 param[i].key.len);

                            *b->last++ = '=';

                            *b->last++ = '"';

                            b->last = ngx_cpymem(b->last, param[i].value.data,

                                                 param[i].value.len);

                            *b->last++ = '"';

                        }

                        *b->last++ = ' ';

                        *b->last++ = '-';

                        *b->last++ = '-';

                        *b->last++ = '>';

                        mctx = ngx_http_get_module_ctx(r->main,

                                                   ngx_http_ssi_filter_module);

                        bl = mctx->blocks->elts;

                        for (ll = &bl[mctx->blocks->nelts - 1].bufs;

                             *ll;

                             ll = &(*ll)->next)

                        {

                            /* void */

                        }

                        *ll = cl;

                        b = NULL;

                        continue;

                    }

                    if (cmd->conditional == 0) {

                        continue;

                    }

                }

                if (cmd->conditional

                    && (ctx->conditional == 0

                        || ctx->conditional > cmd->conditional))

                {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "invalid context of SSI command: \"%V\"",

                                  &ctx->command);

                    goto ssi_error;

                }

                if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "too many SSI command parameters: \"%V\"",

                                  &ctx->command);

                    goto ssi_error;

                }

                ngx_memzero(params,

                           (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));

                param = ctx->params.elts;

                for (i = 0; i < ctx->params.nelts; i++) {

                    for (prm = cmd->params; prm->name.len; prm++) {

                        if (param[i].key.len != prm->name.len

                            || ngx_strncmp(param[i].key.data, prm->name.data,

                                           prm->name.len) != 0)

                        {

                            continue;

                        }

                        if (!prm->multiple) {

                            if (params[prm->index]) {

                                ngx_log_error(NGX_LOG_ERR,

                                              r->connection->log, 0,

                                              "duplicate \"%V\" parameter "

                                              "in \"%V\" SSI command",

                                              &param[i].key, &ctx->command);

                                goto ssi_error;

                            }

                            params[prm->index] = &param[i].value;

                            break;

                        }

                        for (index = prm->index; params[index]; index++) {

                            /* void */

                        }

                        params[index] = &param[i].value;

                        break;

                    }

                    if (prm->name.len == 0) {

                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                      "invalid parameter name: \"%V\" "

                                      "in \"%V\" SSI command",

                                      &param[i].key, &ctx->command);

                        goto ssi_error;

                    }

                }

                for (prm = cmd->params; prm->name.len; prm++) {

                    if (prm->mandatory && params[prm->index] == 0) {

                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                      "mandatory \"%V\" parameter is absent "

                                      "in \"%V\" SSI command",

                                      &prm->name, &ctx->command);

                        goto ssi_error;

                    }

                }

                if (cmd->flush && ctx->out) {

                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                                   "ssi flush");

                    if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {

                        return NGX_ERROR;

                    }

                }

                rc = cmd->handler(r, ctx, params);

                if (rc == NGX_OK) {

                    continue;

                }

                if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {

                    ngx_http_ssi_buffered(r, ctx);

                    return rc;

                }

            }

            /* rc == NGX_HTTP_SSI_ERROR */

    ssi_error:

            if (slcf->silent_errors) {

                continue;

            }

            if (ctx->free) {

                cl = ctx->free;

                ctx->free = ctx->free->next;

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

            } else {

                b = ngx_calloc_buf(r->pool);

                if (b == NULL) {

                    return NGX_ERROR;

                }

                cl = ngx_alloc_chain_link(r->pool);

                if (cl == NULL) {

                    return NGX_ERROR;

                }

                cl->buf = b;

            }

            b->memory = 1;

            b->pos = ctx->errmsg.data;

            b->last = ctx->errmsg.data + ctx->errmsg.len;

            cl->next = NULL;

            *ctx->last_out = cl;

            ctx->last_out = &cl->next;

            continue;

        }

        if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {

            if (b == NULL) {

                if (ctx->free) {

                    cl = ctx->free;

                    ctx->free = ctx->free->next;

                    b = cl->buf;

                    ngx_memzero(b, sizeof(ngx_buf_t));

                } else {

                    b = ngx_calloc_buf(r->pool);

                    if (b == NULL) {

                        return NGX_ERROR;

                    }

                    cl = ngx_alloc_chain_link(r->pool);

                    if (cl == NULL) {

                        return NGX_ERROR;

                    }

                    cl->buf = b;

                }

                b->sync = 1;

                cl->next = NULL;

                *ctx->last_out = cl;

                ctx->last_out = &cl->next;

            }

            b->last_buf = ctx->buf->last_buf;

            b->shadow = ctx->buf;

            if (slcf->ignore_recycled_buffers == 0)  {

                b->recycled = ctx->buf->recycled;

            }

        }

        ctx->buf = NULL;

        ctx->saved = ctx->looked;

    }

    if (ctx->out == NULL && ctx->busy == NULL) {

        return NGX_OK;

    }

    return ngx_http_ssi_output(r, ctx);

}

static ngx_int_t

‌ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)

{

    ngx_int_t     rc;

    ngx_buf_t    *b;

    ngx_chain_t  *cl;

#if 1

    b = NULL;

    for (cl = ctx->out; cl; cl = cl->next) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "ssi out: %p %p", cl->buf, cl->buf->pos);

        if (cl->buf == b) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                          "the same buf was used in ssi");

            ngx_debug_point();

            return NGX_ERROR;

        }

        b = cl->buf;

    }

#endif

    rc = ngx_http_next_body_filter(r, ctx->out);

    if (ctx->busy == NULL) {

        ctx->busy = ctx->out;

    } else {

        for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }

        cl->next = ctx->out;

    }

    ctx->out = NULL;

    ctx->last_out = &ctx->out;

    while (ctx->busy) {

        cl = ctx->busy;

        b = cl->buf;

        if (ngx_buf_size(b) != 0) {

            break;

        }

        if (b->shadow) {

            b->shadow->pos = b->shadow->last;

        }

        ctx->busy = cl->next;

        if (ngx_buf_in_memory(b) || b->in_file) {

            /* add data bufs only to the free buf chain */

            cl->next = ctx->free;

            ctx->free = cl;

        }

    }

    ngx_http_ssi_buffered(r, ctx);

    return rc;

}

static void

‌ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)

{

    if (ctx->in || ctx->buf) {

        r->buffered |= NGX_HTTP_SSI_BUFFERED;

    } else {

        r->buffered &= ~NGX_HTTP_SSI_BUFFERED;

    }

}

static ngx_int_t

‌ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)

{

    u_char                *p, *value, *last, *copy_end, ch;

    size_t                 looked;

    ngx_http_ssi_state_e   state;

    state = ctx->state;

    looked = ctx->looked;

    last = ctx->buf->last;

    copy_end = ctx->copy_end;

    for (p = ctx->pos; p < last; p++) {

        ch = *p;

        if (state == ssi_start_state) {

            /* the tight loop */

            for ( ;; ) {

                if (ch == '<') {

                    copy_end = p;

                    looked = 1;

                    state = ssi_tag_state;

                    goto tag_started;

                }

                if (++p == last) {

                    break;

                }

                ch = *p;

            }

            ctx->state = state;

            ctx->pos = p;

            ctx->looked = looked;

            ctx->copy_end = p;

            if (ctx->copy_start == NULL) {

                ctx->copy_start = ctx->buf->pos;

            }

            return NGX_AGAIN;

        tag_started:

            continue;

        }

        switch (state) {

        case ssi_start_state:

            /* not reached */

            break;

        case ssi_tag_state:

            switch (ch) {

            case '!':

                looked = 2;

                state = ssi_comment0_state;

                break;

            case '<':

                copy_end = p;

                break;

            default:

                copy_end = p;

                looked = 0;

                state = ssi_start_state;

                break;

            }

            break;

        case ssi_comment0_state:

            switch (ch) {

            case '-':

                looked = 3;

                state = ssi_comment1_state;

                break;

            case '<':

                copy_end = p;

                looked = 1;

                state = ssi_tag_state;

                break;

            default:

                copy_end = p;

                looked = 0;

                state = ssi_start_state;

                break;

            }

            break;

        case ssi_comment1_state:

            switch (ch) {

            case '-':

                looked = 4;

                state = ssi_sharp_state;

                break;

            case '<':

                copy_end = p;

                looked = 1;

                state = ssi_tag_state;

                break;

            default:

                copy_end = p;

                looked = 0;

                state = ssi_start_state;

                break;

            }

            break;

        case ssi_sharp_state:

            switch (ch) {

            case '#':

                if (p - ctx->pos < 4) {

                    ctx->saved = 0;

                }

                looked = 0;

                state = ssi_precommand_state;

                break;

            case '<':

                copy_end = p;

                looked = 1;

                state = ssi_tag_state;

                break;

            default:

                copy_end = p;

                looked = 0;

                state = ssi_start_state;

                break;

            }

            break;

        case ssi_precommand_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                break;

            default:

                ctx->command.len = 1;

                ctx->command.data = ngx_pnalloc(r->pool,

                                                NGX_HTTP_SSI_COMMAND_LEN);

                if (ctx->command.data == NULL) {

                    return NGX_ERROR;

                }

                ctx->command.data[0] = ch;

                ctx->key = 0;

                ctx->key = ngx_hash(ctx->key, ch);

                ctx->params.nelts = 0;

                state = ssi_command_state;

                break;

            }

            break;

        case ssi_command_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                state = ssi_preparam_state;

                break;

            case '-':

                state = ssi_comment_end0_state;

                break;

            default:

                if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "the \"%V%c...\" SSI command is too long",

                                  &ctx->command, ch);

                    state = ssi_error_state;

                    break;

                }

                ctx->command.data[ctx->command.len++] = ch;

                ctx->key = ngx_hash(ctx->key, ch);

            }

            break;

        case ssi_preparam_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                break;

            case '-':

                state = ssi_comment_end0_state;

                break;

            default:

                ctx->param = ngx_array_push(&ctx->params);

                if (ctx->param == NULL) {

                    return NGX_ERROR;

                }

                ctx->param->key.len = 1;

                ctx->param->key.data = ngx_pnalloc(r->pool,

                                                   NGX_HTTP_SSI_PARAM_LEN);

                if (ctx->param->key.data == NULL) {

                    return NGX_ERROR;

                }

                ctx->param->key.data[0] = ch;

                ctx->param->value.len = 0;

                if (ctx->value_buf == NULL) {

                    ctx->param->value.data = ngx_pnalloc(r->pool,

                                                         ctx->value_len + 1);

                    if (ctx->param->value.data == NULL) {

                        return NGX_ERROR;

                    }

                } else {

                    ctx->param->value.data = ctx->value_buf;

                }

                state = ssi_param_state;

                break;

            }

            break;

        case ssi_param_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                state = ssi_preequal_state;

                break;

            case '=':

                state = ssi_prevalue_state;

                break;

            case '-':

                state = ssi_error_end0_state;

                ctx->param->key.data[ctx->param->key.len++] = ch;

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "invalid \"%V\" parameter in \"%V\" SSI command",

                              &ctx->param->key, &ctx->command);

                break;

            default:

                if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {

                    state = ssi_error_state;

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "too long \"%V%c...\" parameter in "

                                  "\"%V\" SSI command",

                                  &ctx->param->key, ch, &ctx->command);

                    break;

                }

                ctx->param->key.data[ctx->param->key.len++] = ch;

            }

            break;

        case ssi_preequal_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                break;

            case '=':

                state = ssi_prevalue_state;

                break;

            default:

                if (ch == '-') {

                    state = ssi_error_end0_state;

                } else {

                    state = ssi_error_state;

                }

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "unexpected \"%c\" symbol after \"%V\" "

                              "parameter in \"%V\" SSI command",

                              ch, &ctx->param->key, &ctx->command);

                break;

            }

            break;

        case ssi_prevalue_state:

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                break;

            case '"':

                state = ssi_double_quoted_value_state;

                break;

            case '\'':

                state = ssi_quoted_value_state;

                break;

            default:

                if (ch == '-') {

                    state = ssi_error_end0_state;

                } else {

                    state = ssi_error_state;

                }

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "unexpected \"%c\" symbol before value of "

                              "\"%V\" parameter in \"%V\" SSI command",

                              ch, &ctx->param->key, &ctx->command);

                break;

            }

            break;

        case ssi_double_quoted_value_state:

            switch (ch) {

            case '"':

                state = ssi_postparam_state;

                break;

            case '\\':

                ctx->saved_state = ssi_double_quoted_value_state;

                state = ssi_quoted_symbol_state;

                /* fall through */

            default:

                if (ctx->param->value.len == ctx->value_len) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "too long \"%V%c...\" value of \"%V\" "

                                  "parameter in \"%V\" SSI command",

                                  &ctx->param->value, ch, &ctx->param->key,

                                  &ctx->command);

                    state = ssi_error_state;

                    break;

                }

                ctx->param->value.data[ctx->param->value.len++] = ch;

            }

            break;

        case ssi_quoted_value_state:

            switch (ch) {

            case '\'':

                state = ssi_postparam_state;

                break;

            case '\\':

                ctx->saved_state = ssi_quoted_value_state;

                state = ssi_quoted_symbol_state;

                /* fall through */

            default:

                if (ctx->param->value.len == ctx->value_len) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "too long \"%V%c...\" value of \"%V\" "

                                  "parameter in \"%V\" SSI command",

                                  &ctx->param->value, ch, &ctx->param->key,

                                  &ctx->command);

                    state = ssi_error_state;

                    break;

                }

                ctx->param->value.data[ctx->param->value.len++] = ch;

            }

            break;

        case ssi_quoted_symbol_state:

            state = ctx->saved_state;

            if (ctx->param->value.len == ctx->value_len) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "too long \"%V%c...\" value of \"%V\" "

                              "parameter in \"%V\" SSI command",

                              &ctx->param->value, ch, &ctx->param->key,

                              &ctx->command);

                state = ssi_error_state;

                break;

            }

            ctx->param->value.data[ctx->param->value.len++] = ch;

            break;

        case ssi_postparam_state:

            if (ctx->param->value.len + 1 < ctx->value_len / 2) {

                value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);

                if (value == NULL) {

                    return NGX_ERROR;

                }

                ngx_memcpy(value, ctx->param->value.data,

                           ctx->param->value.len);

                ctx->value_buf = ctx->param->value.data;

                ctx->param->value.data = value;

            } else {

                ctx->value_buf = NULL;

            }

            switch (ch) {

            case ' ':

            case CR:

            case LF:

            case '\t':

                state = ssi_preparam_state;

                break;

            case '-':

                state = ssi_comment_end0_state;

                break;

            default:

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "unexpected \"%c\" symbol after \"%V\" value "

                              "of \"%V\" parameter in \"%V\" SSI command",

                              ch, &ctx->param->value, &ctx->param->key,

                              &ctx->command);

                state = ssi_error_state;

                break;

            }

            break;

        case ssi_comment_end0_state:

            switch (ch) {

            case '-':

                state = ssi_comment_end1_state;

                break;

            default:

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "unexpected \"%c\" symbol in \"%V\" SSI command",

                              ch, &ctx->command);

                state = ssi_error_state;

                break;

            }

            break;

        case ssi_comment_end1_state:

            switch (ch) {

            case '>':

                ctx->state = ssi_start_state;

                ctx->pos = p + 1;

                ctx->looked = looked;

                ctx->copy_end = copy_end;

                if (ctx->copy_start == NULL && copy_end) {

                    ctx->copy_start = ctx->buf->pos;

                }

                return NGX_OK;

            default:

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "unexpected \"%c\" symbol in \"%V\" SSI command",

                              ch, &ctx->command);

                state = ssi_error_state;

                break;

            }

            break;

        case ssi_error_state:

            switch (ch) {

            case '-':

                state = ssi_error_end0_state;

                break;

            default:

                break;

            }

            break;

        case ssi_error_end0_state:

            switch (ch) {

            case '-':

                state = ssi_error_end1_state;

                break;

            default:

                state = ssi_error_state;

                break;

            }

            break;

        case ssi_error_end1_state:

            switch (ch) {

            case '>':

                ctx->state = ssi_start_state;

                ctx->pos = p + 1;

                ctx->looked = looked;

                ctx->copy_end = copy_end;

                if (ctx->copy_start == NULL && copy_end) {

                    ctx->copy_start = ctx->buf->pos;

                }

                return NGX_HTTP_SSI_ERROR;

            default:

                state = ssi_error_state;

                break;

            }

            break;

        }

    }

    ctx->state = state;

    ctx->pos = p;

    ctx->looked = looked;

    ctx->copy_end = (state == ssi_start_state) ? p : copy_end;

    if (ctx->copy_start == NULL && ctx->copy_end) {

        ctx->copy_start = ctx->buf->pos;

    }

    return NGX_AGAIN;

}

static ngx_str_t *

‌ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,

    ngx_uint_t key)

{

    ngx_uint_t           i;

    ngx_list_part_t     *part;

    ngx_http_ssi_var_t  *var;

    ngx_http_ssi_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

#if (NGX_PCRE)

    {

    ngx_str_t  *value;

    if (key >= '0' && key <= '9') {

        i = key - '0';

        if (i < ctx->ncaptures) {

            value = ngx_palloc(r->pool, sizeof(ngx_str_t));

            if (value == NULL) {

                return NULL;

            }

            i *= 2;

            value->data = ctx->captures_data + ctx->captures[i];

            value->len = ctx->captures[i + 1] - ctx->captures[i];

            return value;

        }

    }

    }

#endif

    if (ctx->variables == NULL) {

        return NULL;

    }

    part = &ctx->variables->part;

    var = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {

            if (part->next == NULL) {

                break;

            }

            part = part->next;

            var = part->elts;

            i = 0;

        }

        if (name->len != var[i].name.len) {

            continue;

        }

        if (key != var[i].key) {

            continue;

        }

        if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {

            return &var[i].value;

        }

    }

    return NULL;

}

static ngx_int_t

‌ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t *text, ngx_uint_t flags)

{

    u_char                      ch, *p, **value, *data, *part_data;

    size_t                     *size, len, prefix, part_len;

    ngx_str_t                   var, *val;

    ngx_int_t                   key;

    ngx_uint_t                  i, n, bracket, quoted;

    ngx_array_t                 lengths, values;

    ngx_http_variable_value_t  *vv;

    n = ngx_http_script_variables_count(text);

    if (n == 0) {

        data = text->data;

        p = data;

        if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {

            for (prefix = r->uri.len; prefix; prefix--) {

                if (r->uri.data[prefix - 1] == '/') {

                    break;

                }

            }

            if (prefix) {

                len = prefix + text->len;

                data = ngx_pnalloc(r->pool, len);

                if (data == NULL) {

                    return NGX_ERROR;

                }

                p = ngx_copy(data, r->uri.data, prefix);

            }

        }

        quoted = 0;

        for (i = 0; i < text->len; i++) {

            ch = text->data[i];

            if (!quoted) {

                if (ch == '\\') {

                    quoted = 1;

                    continue;

                }

            } else {

                quoted = 0;

                if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {

                    *p++ = '\\';

                }

            }

            *p++ = ch;

        }

        text->len = p - data;

        text->data = data;

        return NGX_OK;

    }

    if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {

        return NGX_ERROR;

    }

    len = 0;

    i = 0;

    while (i < text->len) {

        if (text->data[i] == '$') {

            var.len = 0;

            if (++i == text->len) {

                goto invalid_variable;

            }

            if (text->data[i] == '{') {

                bracket = 1;

                if (++i == text->len) {

                    goto invalid_variable;

                }

                var.data = &text->data[i];

            } else {

                bracket = 0;

                var.data = &text->data[i];

            }

            for ( /* void */ ; i < text->len; i++, var.len++) {

                ch = text->data[i];

                if (ch == '}' && bracket) {

                    i++;

                    bracket = 0;

                    break;

                }

                if ((ch >= 'A' && ch <= 'Z')

                    || (ch >= 'a' && ch <= 'z')

                    || (ch >= '0' && ch <= '9')

                    || ch == '_')

                {

                    continue;

                }

                break;

            }

            if (bracket) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "the closing bracket in \"%V\" "

                              "variable is missing", &var);

                return NGX_HTTP_SSI_ERROR;

            }

            if (var.len == 0) {

                goto invalid_variable;

            }

            key = ngx_hash_strlow(var.data, var.data, var.len);

            val = ngx_http_ssi_get_variable(r, &var, key);

            if (val == NULL) {

                vv = ngx_http_get_variable(r, &var, key);

                if (vv == NULL) {

                    return NGX_ERROR;

                }

                if (vv->not_found) {

                    continue;

                }

                part_data = vv->data;

                part_len = vv->len;

            } else {

                part_data = val->data;

                part_len = val->len;

            }

        } else {

            part_data = &text->data[i];

            quoted = 0;

            for (p = part_data; i < text->len; i++) {

                ch = text->data[i];

                if (!quoted) {

                    if (ch == '\\') {

                        quoted = 1;

                        continue;

                    }

                    if (ch == '$') {

                        break;

                    }

                } else {

                    quoted = 0;

                    if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {

                        *p++ = '\\';

                    }

                }

                *p++ = ch;

            }

            part_len = p - part_data;

        }

        len += part_len;

        size = ngx_array_push(&lengths);

        if (size == NULL) {

            return NGX_ERROR;

        }

        *size = part_len;

        value = ngx_array_push(&values);

        if (value == NULL) {

            return NGX_ERROR;

        }

        *value = part_data;

    }

    prefix = 0;

    size = lengths.elts;

    value = values.elts;

    if (flags & NGX_HTTP_SSI_ADD_PREFIX) {

        for (i = 0; i < values.nelts; i++) {

            if (size[i] != 0) {

                if (*value[i] != '/') {

                    for (prefix = r->uri.len; prefix; prefix--) {

                        if (r->uri.data[prefix - 1] == '/') {

                            len += prefix;

                            break;

                        }

                    }

                }

                break;

            }

        }

    }

    p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));

    if (p == NULL) {

        return NGX_ERROR;

    }

    text->len = len;

    text->data = p;

    p = ngx_copy(p, r->uri.data, prefix);

    for (i = 0; i < values.nelts; i++) {

        p = ngx_copy(p, value[i], size[i]);

    }

    return NGX_OK;

invalid_variable:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "invalid variable name in \"%V\"", text);

    return NGX_HTTP_SSI_ERROR;

}

static ngx_int_t

‌ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,

    ngx_str_t *str)

{

#if (NGX_PCRE)

    int                   rc, *captures;

    u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];

    size_t                size;

    ngx_int_t             key;

    ngx_str_t            *vv, name, value;

    ngx_uint_t            i, n;

    ngx_http_ssi_ctx_t   *ctx;

    ngx_http_ssi_var_t   *var;

    ngx_regex_compile_t   rgc;

    ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));

    rgc.pattern = *pattern;

    rgc.pool = r->pool;

    rgc.err.len = NGX_MAX_CONF_ERRSTR;

    rgc.err.data = errstr;

    if (ngx_regex_compile(&rgc) != NGX_OK) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);

        return NGX_HTTP_SSI_ERROR;

    }

    n = (rgc.captures + 1) * 3;

    captures = ngx_palloc(r->pool, n * sizeof(int));

    if (captures == NULL) {

        return NGX_ERROR;

    }

    rc = ngx_regex_exec(rgc.regex, str, captures, n);

    if (rc < NGX_REGEX_NO_MATCHED) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",

                      rc, str, pattern);

        return NGX_HTTP_SSI_ERROR;

    }

    if (rc == NGX_REGEX_NO_MATCHED) {

        return NGX_DECLINED;

    }

    ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

    ctx->ncaptures = rc;

    ctx->captures = captures;

    ctx->captures_data = str->data;

    if (rgc.named_captures > 0) {

        if (ctx->variables == NULL) {

            ctx->variables = ngx_list_create(r->pool, 4,

                                             sizeof(ngx_http_ssi_var_t));

            if (ctx->variables == NULL) {

                return NGX_ERROR;

            }

        }

        size = rgc.name_size;

        p = rgc.names;

        for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {

            name.data = &p[2];

            name.len = ngx_strlen(name.data);

            n = 2 * ((p[0] << 8) + p[1]);

            value.data = &str->data[captures[n]];

            value.len = captures[n + 1] - captures[n];

            key = ngx_hash_strlow(name.data, name.data, name.len);

            vv = ngx_http_ssi_get_variable(r, &name, key);

            if (vv) {

                *vv = value;

                continue;

            }

            var = ngx_list_push(ctx->variables);

            if (var == NULL) {

                return NGX_ERROR;

            }

            var->name = name;

            var->key = key;

            var->value = value;

        }

    }

    return NGX_OK;

#else

    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                  "the using of the regex \"%V\" in SSI requires PCRE library",

                  pattern);

    return NGX_HTTP_SSI_ERROR;

#endif

}

static ngx_int_t

‌ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_int_t                    rc, key;

    ngx_str_t                   *uri, *file, *wait, *set, *stub, args;

    ngx_buf_t                   *b;

    ngx_uint_t                   flags, i;

    ngx_chain_t                 *cl, *tl, **ll, *out;

    ngx_http_request_t          *sr;

    ngx_http_ssi_var_t          *var;

    ngx_http_ssi_ctx_t          *mctx;

    ngx_http_ssi_block_t        *bl;

    ngx_http_post_subrequest_t  *psr;

    uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];

    file = params[NGX_HTTP_SSI_INCLUDE_FILE];

    wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];

    set = params[NGX_HTTP_SSI_INCLUDE_SET];

    stub = params[NGX_HTTP_SSI_INCLUDE_STUB];

    if (uri && file) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "inlcusion may be either virtual=\"%V\" or file=\"%V\"",

                      uri, file);

        return NGX_HTTP_SSI_ERROR;

    }

    if (uri == NULL && file == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "no parameter in \"include\" SSI command");

        return NGX_HTTP_SSI_ERROR;

    }

    if (set && stub) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "\"set\" and \"stub\" cannot be used together "

                      "in \"include\" SSI command");

        return NGX_HTTP_SSI_ERROR;

    }

    if (wait) {

        if (uri == NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"wait\" cannot be used with file=\"%V\"", file);

            return NGX_HTTP_SSI_ERROR;

        }

        if (wait->len == 2

            && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)

        {

            wait = NULL;

        } else if (wait->len != 3

                   || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)

        {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "invalid value \"%V\" in the \"wait\" parameter",

                          wait);

            return NGX_HTTP_SSI_ERROR;

        }

    }

    if (uri == NULL) {

        uri = file;

        wait = (ngx_str_t *) -1;

    }

    rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);

    if (rc != NGX_OK) {

        return rc;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi include: \"%V\"", uri);

    ngx_str_null(&args);

    flags = NGX_HTTP_LOG_UNSAFE;

    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {

        return NGX_HTTP_SSI_ERROR;

    }

    psr = NULL;

    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

    if (stub) {

        if (mctx->blocks) {

            bl = mctx->blocks->elts;

            for (i = 0; i < mctx->blocks->nelts; i++) {

                if (stub->len == bl[i].name.len

                    && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)

                {

                    goto found;

                }

            }

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "\"stub\"=\"%V\" for \"include\" not found", stub);

        return NGX_HTTP_SSI_ERROR;

    found:

        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));

        if (psr == NULL) {

            return NGX_ERROR;

        }

        psr->handler = ngx_http_ssi_stub_output;

        if (bl[i].count++) {

            out = NULL;

            ll = &out;

            for (tl = bl[i].bufs; tl; tl = tl->next) {

                if (ctx->free) {

                    cl = ctx->free;

                    ctx->free = ctx->free->next;

                    b = cl->buf;

                } else {

                    b = ngx_alloc_buf(r->pool);

                    if (b == NULL) {

                        return NGX_ERROR;

                    }

                    cl = ngx_alloc_chain_link(r->pool);

                    if (cl == NULL) {

                        return NGX_ERROR;

                    }

                    cl->buf = b;

                }

                ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));

                b->pos = b->start;

                *ll = cl;

                cl->next = NULL;

                ll = &cl->next;

            }

            psr->data = out;

        } else {

            psr->data = bl[i].bufs;

        }

    }

    if (wait) {

        flags |= NGX_HTTP_SUBREQUEST_WAITED;

    }

    if (set) {

        key = ngx_hash_strlow(set->data, set->data, set->len);

        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));

        if (psr == NULL) {

            return NGX_ERROR;

        }

        psr->handler = ngx_http_ssi_set_variable;

        psr->data = ngx_http_ssi_get_variable(r, set, key);

        if (psr->data == NULL) {

            if (mctx->variables == NULL) {

                mctx->variables = ngx_list_create(r->pool, 4,

                                                  sizeof(ngx_http_ssi_var_t));

                if (mctx->variables == NULL) {

                    return NGX_ERROR;

                }

            }

            var = ngx_list_push(mctx->variables);

            if (var == NULL) {

                return NGX_ERROR;

            }

            var->name = *set;

            var->key = key;

            var->value = ngx_http_ssi_null_string;

            psr->data = &var->value;

        }

        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;

    }

    if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {

        return NGX_HTTP_SSI_ERROR;

    }

    if (wait == NULL && set == NULL) {

        return NGX_OK;

    }

    if (ctx->wait == NULL) {

        ctx->wait = sr;

        return NGX_AGAIN;

    } else {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "can only wait for one subrequest at a time");

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)

{

    ngx_chain_t  *out;

    if (rc == NGX_ERROR || r->connection->error || r->request_output) {

        return rc;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi stub output: \"%V?%V\"", &r->uri, &r->args);

    out = data;

    if (!r->header_sent) {

        r->headers_out.content_type_len =

                                      r->parent->headers_out.content_type_len;

        r->headers_out.content_type = r->parent->headers_out.content_type;

        if (ngx_http_send_header(r) == NGX_ERROR) {

            return NGX_ERROR;

        }

    }

    return ngx_http_output_filter(r, out);

}

static ngx_int_t

‌ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)

{

    ngx_str_t  *value = data;

    if (r->upstream) {

        value->len = r->upstream->buffer.last - r->upstream->buffer.pos;

        value->data = r->upstream->buffer.pos;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    u_char                     *p;

    uintptr_t                   len;

    ngx_int_t                   key;

    ngx_buf_t                  *b;

    ngx_str_t                  *var, *value, *enc, text;

    ngx_chain_t                *cl;

    ngx_http_variable_value_t  *vv;

    var = params[NGX_HTTP_SSI_ECHO_VAR];

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi echo \"%V\"", var);

    key = ngx_hash_strlow(var->data, var->data, var->len);

    value = ngx_http_ssi_get_variable(r, var, key);

    if (value == NULL) {

        vv = ngx_http_get_variable(r, var, key);

        if (vv == NULL) {

            return NGX_HTTP_SSI_ERROR;

        }

        if (!vv->not_found) {

            text.data = vv->data;

            text.len = vv->len;

            value = &text;

        }

    }

    if (value == NULL) {

        value = params[NGX_HTTP_SSI_ECHO_DEFAULT];

        if (value == NULL) {

            value = &ngx_http_ssi_none;

        } else if (value->len == 0) {

            return NGX_OK;

        }

    } else {

        if (value->len == 0) {

            return NGX_OK;

        }

    }

    enc = params[NGX_HTTP_SSI_ECHO_ENCODING];

    if (enc) {

        if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {

            ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;

        } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {

            ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;

        } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {

            ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;

        } else {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "unknown encoding \"%V\" in the \"echo\" command",

                          enc);

        }

    }

    p = value->data;

    switch (ctx->encoding) {

    case NGX_HTTP_SSI_URL_ENCODING:

        len = 2 * ngx_escape_uri(NULL, value->data, value->len,

                                 NGX_ESCAPE_HTML);

        if (len) {

            p = ngx_pnalloc(r->pool, value->len + len);

            if (p == NULL) {

                return NGX_HTTP_SSI_ERROR;

            }

            (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);

        }

        len += value->len;

        break;

    case NGX_HTTP_SSI_ENTITY_ENCODING:

        len = ngx_escape_html(NULL, value->data, value->len);

        if (len) {

            p = ngx_pnalloc(r->pool, value->len + len);

            if (p == NULL) {

                return NGX_HTTP_SSI_ERROR;

            }

            (void) ngx_escape_html(p, value->data, value->len);

        }

        len += value->len;

        break;

    default: /* NGX_HTTP_SSI_NO_ENCODING */

        len = value->len;

        break;

    }

    b = ngx_calloc_buf(r->pool);

    if (b == NULL) {

        return NGX_HTTP_SSI_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_HTTP_SSI_ERROR;

    }

    b->memory = 1;

    b->pos = p;

    b->last = p + len;

    cl->buf = b;

    cl->next = NULL;

    *ctx->last_out = cl;

    ctx->last_out = &cl->next;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_str_t  *value;

    value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];

    if (value) {

        ctx->timefmt.len = value->len;

        ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);

        if (ctx->timefmt.data == NULL) {

            return NGX_HTTP_SSI_ERROR;

        }

        ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);

    }

    value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];

    if (value) {

        ctx->errmsg = *value;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_int_t            key, rc;

    ngx_str_t           *name, *value, *vv;

    ngx_http_ssi_var_t  *var;

    ngx_http_ssi_ctx_t  *mctx;

    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

    if (mctx->variables == NULL) {

        mctx->variables = ngx_list_create(r->pool, 4,

                                          sizeof(ngx_http_ssi_var_t));

        if (mctx->variables == NULL) {

            return NGX_ERROR;

        }

    }

    name = params[NGX_HTTP_SSI_SET_VAR];

    value = params[NGX_HTTP_SSI_SET_VALUE];

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi set \"%V\" \"%V\"", name, value);

    rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);

    if (rc != NGX_OK) {

        return rc;

    }

    key = ngx_hash_strlow(name->data, name->data, name->len);

    vv = ngx_http_ssi_get_variable(r, name, key);

    if (vv) {

        *vv = *value;

        return NGX_OK;

    }

    var = ngx_list_push(mctx->variables);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->name = *name;

    var->key = key;

    var->value = *value;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "set: \"%V\"=\"%V\"", name, value);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    u_char       *p, *last;

    ngx_str_t    *expr, left, right;

    ngx_int_t     rc;

    ngx_uint_t    negative, noregex, flags;

    if (ctx->command.len == 2) {

        if (ctx->conditional) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "the \"if\" command inside the \"if\" command");

            return NGX_HTTP_SSI_ERROR;

        }

    }

    if (ctx->output_chosen) {

        ctx->output = 0;

        return NGX_OK;

    }

    expr = params[NGX_HTTP_SSI_IF_EXPR];

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi if expr=\"%V\"", expr);

    left.data = expr->data;

    last = expr->data + expr->len;

    for (p = left.data; p < last; p++) {

        if (*p >= 'A' && *p <= 'Z') {

            *p |= 0x20;

            continue;

        }

        if ((*p >= 'a' && *p <= 'z')

             || (*p >= '0' && *p <= '9')

             || *p == '$' || *p == '{' || *p == '}' || *p == '_'

             || *p == '"' || *p == '\'')

        {

            continue;

        }

        break;

    }

    left.len = p - left.data;

    while (p < last && *p == ' ') {

        p++;

    }

    flags = 0;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "left: \"%V\"", &left);

    rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);

    if (rc != NGX_OK) {

        return rc;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "evaluted left: \"%V\"", &left);

    if (p == last) {

        if (left.len) {

            ctx->output = 1;

            ctx->output_chosen = 1;

        } else {

            ctx->output = 0;

        }

        ctx->conditional = NGX_HTTP_SSI_COND_IF;

        return NGX_OK;

    }

    if (p < last && *p == '=') {

        negative = 0;

        p++;

    } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {

        negative = 1;

        p += 2;

    } else {

        goto invalid_expression;

    }

    while (p < last && *p == ' ') {

        p++;

    }

    if (p < last - 1 && *p == '/') {

        if (*(last - 1) != '/') {

            goto invalid_expression;

        }

        noregex = 0;

        flags = NGX_HTTP_SSI_ADD_ZERO;

        last--;

        p++;

    } else {

        noregex = 1;

        flags = 0;

        if (p < last - 1 && p[0] == '\\' && p[1] == '/') {

            p++;

        }

    }

    right.len = last - p;

    right.data = p;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "right: \"%V\"", &right);

    rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);

    if (rc != NGX_OK) {

        return rc;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "evaluted right: \"%V\"", &right);

    if (noregex) {

        if (left.len != right.len) {

            rc = -1;

        } else {

            rc = ngx_strncmp(left.data, right.data, right.len);

        }

    } else {

        right.data[right.len] = '\0';

        rc = ngx_http_ssi_regex_match(r, &right, &left);

        if (rc == NGX_OK) {

            rc = 0;

        } else if (rc == NGX_DECLINED) {

            rc = -1;

        } else {

            return rc;

        }

    }

    if ((rc == 0 && !negative) || (rc != 0 && negative)) {

        ctx->output = 1;

        ctx->output_chosen = 1;

    } else {

        ctx->output = 0;

    }

    ctx->conditional = NGX_HTTP_SSI_COND_IF;

    return NGX_OK;

invalid_expression:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "invalid expression in \"%V\"", expr);

    return NGX_HTTP_SSI_ERROR;

}

static ngx_int_t

‌ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi else");

    if (ctx->output_chosen) {

        ctx->output = 0;

    } else {

        ctx->output = 1;

    }

    ctx->conditional = NGX_HTTP_SSI_COND_ELSE;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi endif");

    ctx->output = 1;

    ctx->output_chosen = 0;

    ctx->conditional = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_http_ssi_ctx_t    *mctx;

    ngx_http_ssi_block_t  *bl;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi block");

    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

    if (mctx->blocks == NULL) {

        mctx->blocks = ngx_array_create(r->pool, 4,

                                        sizeof(ngx_http_ssi_block_t));

        if (mctx->blocks == NULL) {

            return NGX_HTTP_SSI_ERROR;

        }

    }

    bl = ngx_array_push(mctx->blocks);

    if (bl == NULL) {

        return NGX_HTTP_SSI_ERROR;

    }

    bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];

    bl->bufs = NULL;

    bl->count = 0;

    ctx->output = 0;

    ctx->block = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,

    ngx_str_t **params)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "ssi endblock");

    ctx->output = 1;

    ctx->block = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t gmt)

{

    ngx_http_ssi_ctx_t  *ctx;

    ngx_time_t          *tp;

    ngx_str_t           *timefmt;

    struct tm            tm;

    char                 buf[NGX_HTTP_SSI_DATE_LEN];

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    tp = ngx_timeofday();

    ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);

    timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;

    if (timefmt->len == sizeof("%s") - 1

        && timefmt->data[0] == '%' && timefmt->data[1] == 's')

    {

        v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);

        if (v->data == NULL) {

            return NGX_ERROR;

        }

        v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data;

        return NGX_OK;

    }

    if (gmt) {

        ngx_libc_gmtime(tp->sec, &tm);

    } else {

        ngx_libc_localtime(tp->sec, &tm);

    }

    v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,

                      (char *) timefmt->data, &tm);

    if (v->len == 0) {

        return NGX_ERROR;

    }

    v->data = ngx_pnalloc(r->pool, v->len);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(v->data, buf, v->len);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_ssi_preconfiguration(ngx_conf_t *cf)

{

    ngx_int_t                  rc;

    ngx_http_variable_t       *var, *v;

    ngx_http_ssi_command_t    *cmd;

    ngx_http_ssi_main_conf_t  *smcf;

    for (v = ngx_http_ssi_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);

    for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {

        rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,

                              NGX_HASH_READONLY_KEY);

        if (rc == NGX_OK) {

            continue;

        }

        if (rc == NGX_BUSY) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "conflicting SSI command \"%V\"", &cmd->name);

        }

        return NGX_ERROR;

    }

    return NGX_OK;

}

static void *

‌ngx_http_ssi_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_ssi_main_conf_t  *smcf;

    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));

    if (smcf == NULL) {

        return NULL;

    }

    smcf->commands.pool = cf->pool;

    smcf->commands.temp_pool = cf->temp_pool;

    if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {

        return NULL;

    }

    return smcf;

}

static char *

‌ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_ssi_main_conf_t *smcf = conf;

    ngx_hash_init_t  hash;

    hash.hash = &smcf->hash;

    hash.key = ngx_hash_key;

    hash.max_size = 1024;

    hash.bucket_size = ngx_cacheline_size;

    hash.name = "ssi_command_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    if (ngx_hash_init(&hash, smcf->commands.keys.elts,

                      smcf->commands.keys.nelts)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static void *

‌ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_ssi_loc_conf_t  *slcf;

    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));

    if (slcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->types = { NULL };

     *     conf->types_keys = NULL;

     */

    slcf->enable = NGX_CONF_UNSET;

    slcf->silent_errors = NGX_CONF_UNSET;

    slcf->ignore_recycled_buffers = NGX_CONF_UNSET;

    slcf->last_modified = NGX_CONF_UNSET;

    slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;

    slcf->value_len = NGX_CONF_UNSET_SIZE;

    return slcf;

}

static char *

‌ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_ssi_loc_conf_t *prev = parent;

    ngx_http_ssi_loc_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);

    ngx_conf_merge_value(conf->ignore_recycled_buffers,

                         prev->ignore_recycled_buffers, 0);

    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

    ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);

    ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_html_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_ssi_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_ssi_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_ssi_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_ssi_filter_module.h - nginx-1.7.9

Data types defined

ngx_http_ssi_command_pt

ngx_http_ssi_command_t

ngx_http_ssi_ctx_t

ngx_http_ssi_main_conf_t

ngx_http_ssi_param_t

Macros defined

NGX_HTTP_SSI_COMMAND_LEN

NGX_HTTP_SSI_COND_ELSE

NGX_HTTP_SSI_COND_IF

NGX_HTTP_SSI_ENTITY_ENCODING

NGX_HTTP_SSI_MAX_PARAMS

NGX_HTTP_SSI_NO_ENCODING

NGX_HTTP_SSI_PARAMS_N

NGX_HTTP_SSI_PARAM_LEN

NGX_HTTP_SSI_URL_ENCODING

_NGX_HTTP_SSI_FILTER_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_HTTP_SSI_FILTER_H_INCLUDED_

‌#define _NGX_HTTP_SSI_FILTER_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_SSI_MAX_PARAMS       16

‌#define NGX_HTTP_SSI_COMMAND_LEN      32

‌#define NGX_HTTP_SSI_PARAM_LEN        32

‌#define NGX_HTTP_SSI_PARAMS_N         4

‌#define NGX_HTTP_SSI_COND_IF          1

‌#define NGX_HTTP_SSI_COND_ELSE        2

‌#define NGX_HTTP_SSI_NO_ENCODING      0

‌#define NGX_HTTP_SSI_URL_ENCODING     1

‌#define NGX_HTTP_SSI_ENTITY_ENCODING  2

typedef struct {

    ngx_hash_t                hash;

    ngx_hash_keys_arrays_t    commands;

‌} ngx_http_ssi_main_conf_t;

typedef struct {

    ngx_buf_t                *buf;

    u_char                   *pos;

    u_char                   *copy_start;

    u_char                   *copy_end;

    ngx_uint_t                key;

    ngx_str_t                 command;

    ngx_array_t               params;

    ngx_table_elt_t          *param;

    ngx_table_elt_t           params_array[NGX_HTTP_SSI_PARAMS_N];

    ngx_chain_t              *in;

    ngx_chain_t              *out;

    ngx_chain_t             **last_out;

    ngx_chain_t              *busy;

    ngx_chain_t              *free;

    ngx_uint_t                state;

    ngx_uint_t                saved_state;

    size_t                    saved;

    size_t                    looked;

    size_t                    value_len;

    ngx_list_t               *variables;

    ngx_array_t              *blocks;

#if (NGX_PCRE)

    ngx_uint_t                ncaptures;

    int                      *captures;

    u_char                   *captures_data;

#endif

    unsigned                  conditional:2;

    unsigned                  encoding:2;

    unsigned                  block:1;

    unsigned                  output:1;

    unsigned                  output_chosen:1;

    ngx_http_request_t       *wait;

    void                     *value_buf;

    ngx_str_t                 timefmt;

    ngx_str_t                 errmsg;

‌} ngx_http_ssi_ctx_t;

‌typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,

    ngx_http_ssi_ctx_t *ctx, ngx_str_t **);

typedef struct {

    ngx_str_t                 name;

    ngx_uint_t                index;

    unsigned                  mandatory:1;

    unsigned                  multiple:1;

‌} ngx_http_ssi_param_t;

typedef struct {

    ngx_str_t                 name;

    ngx_http_ssi_command_pt   handler;

    ngx_http_ssi_param_t     *params;

    unsigned                  conditional:2;

    unsigned                  block:1;

    unsigned                  flush:1;

‌} ngx_http_ssi_command_t;

extern ngx_module_t  ngx_http_ssi_filter_module;

#endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */

src/http/ngx_http_postpone_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_body_filter

ngx_http_postpone_filter_module

ngx_http_postpone_filter_module_ctx

Functions defined

ngx_http_postpone_filter

ngx_http_postpone_filter_add

ngx_http_postpone_filter_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,

    ngx_chain_t *in);

static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_postpone_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_postpone_filter_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 */

};

‌ngx_module_t  ngx_http_postpone_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_postpone_filter_module_ctx,  /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_connection_t              *c;

    ngx_http_postponed_request_t  *pr;

    c = r->connection;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);

    if (r != c->data) {

        if (in) {

            ngx_http_postpone_filter_add(r, in);

            return NGX_OK;

        }

#if 0

        /* TODO: SSI may pass NULL */

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "http postpone filter NULL inactive request");

#endif

        return NGX_OK;

    }

    if (r->postponed == NULL) {

        if (in || c->buffered) {

            return ngx_http_next_body_filter(r->main, in);

        }

        return NGX_OK;

    }

    if (in) {

        ngx_http_postpone_filter_add(r, in);

    }

    do {

        pr = r->postponed;

        if (pr->request) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http postpone filter wake \"%V?%V\"",

                           &pr->request->uri, &pr->request->args);

            r->postponed = pr->next;

            c->data = pr->request;

            return ngx_http_post_request(pr->request, NULL);

        }

        if (pr->out == NULL) {

            ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                          "http postpone filter NULL output");

        } else {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                           "http postpone filter output \"%V?%V\"",

                           &r->uri, &r->args);

            if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

        r->postponed = pr->next;

    } while (r->postponed);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_http_postponed_request_t  *pr, **ppr;

    if (r->postponed) {

        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }

        if (pr->request == NULL) {

            goto found;

        }

        ppr = &pr->next;

    } else {

        ppr = &r->postponed;

    }

    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));

    if (pr == NULL) {

        return NGX_ERROR;

    }

    *ppr = pr;

    pr->request = NULL;

    pr->out = NULL;

    pr->next = NULL;

found:

    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {

        return NGX_OK;

    }

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_postpone_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_postpone_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_degradation_module.c - nginx-1.7.9

Global variables defined

ngx_http_degradation_commands

ngx_http_degradation_module

ngx_http_degradation_module_ctx

ngx_http_degrade

Data types defined

ngx_http_degradation_loc_conf_t

ngx_http_degradation_main_conf_t

Functions defined

ngx_http_degradation

ngx_http_degradation_create_loc_conf

ngx_http_degradation_create_main_conf

ngx_http_degradation_handler

ngx_http_degradation_init

ngx_http_degradation_merge_loc_conf

ngx_http_degraded

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    size_t      sbrk_size;

‌} ngx_http_degradation_main_conf_t;

typedef struct {

    ngx_uint_t  degrade;

‌} ngx_http_degradation_loc_conf_t;

‌static ngx_conf_enum_t  ngx_http_degrade[] = {

    { ngx_string("204"), 204 },

    { ngx_string("444"), 444 },

    { ngx_null_string, 0 }

};

static void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_degradation_commands[] = {

    { ngx_string("degradation"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_http_degradation,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("degrade"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_degradation_loc_conf_t, degrade),

      &ngx_http_degrade },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_degradation_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_degradation_init,             /* postconfiguration */

    ngx_http_degradation_create_main_conf, /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_degradation_create_loc_conf,  /* create location configuration */

    ngx_http_degradation_merge_loc_conf    /* merge location configuration */

};

‌ngx_module_t  ngx_http_degradation_module = {

    NGX_MODULE_V1,

    &ngx_http_degradation_module_ctx,      /* module context */

    ngx_http_degradation_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_degradation_handler(ngx_http_request_t *r)

{

    ngx_http_degradation_loc_conf_t  *dlcf;

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);

    if (dlcf->degrade && ngx_http_degraded(r)) {

        return dlcf->degrade;

    }

    return NGX_DECLINED;

}

ngx_uint_t

‌ngx_http_degraded(ngx_http_request_t *r)

{

    time_t                             now;

    ngx_uint_t                         log;

    static size_t                      sbrk_size;

    static time_t                      sbrk_time;

    ngx_http_degradation_main_conf_t  *dmcf;

    dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);

    if (dmcf->sbrk_size) {

        log = 0;

        now = ngx_time();

        /* lock mutex */

        if (now != sbrk_time) {

            /*

             * ELF/i386 is loaded at 0x08000000, 128M

             * ELF/amd64 is loaded at 0x00400000, 4M

             *

             * use a function address to subtract the loading address

             */

            sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);

            sbrk_time = now;

            log = 1;

        }

        /* unlock mutex */

        if (sbrk_size >= dmcf->sbrk_size) {

            if (log) {

                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,

                              "degradation sbrk:%uzM",

                              sbrk_size / (1024 * 1024));

            }

            return 1;

        }

    }

    return 0;

}

static void *

‌ngx_http_degradation_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_degradation_main_conf_t  *dmcf;

    dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));

    if (dmcf == NULL) {

        return NULL;

    }

    return dmcf;

}

static void *

‌ngx_http_degradation_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_degradation_loc_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->degrade = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_degradation_loc_conf_t  *prev = parent;

    ngx_http_degradation_loc_conf_t  *conf = child;

    ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_degradation_main_conf_t  *dmcf = conf;

    ngx_str_t  *value, s;

    value = cf->args->elts;

    if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) {

        s.len = value[1].len - 5;

        s.data = value[1].data + 5;

        dmcf->sbrk_size = ngx_parse_size(&s);

        if (dmcf->sbrk_size == (size_t) NGX_ERROR) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid sbrk size \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid parameter \"%V\"", &value[1]);

    return NGX_CONF_ERROR;

}

static ngx_int_t

‌ngx_http_degradation_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_PREACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_degradation_handler;

    return NGX_OK;

}

src/os/unix/ngx_freebsd_config.h - nginx-1.7.9

Data types defined

ngx_aiocb_t

Macros defined

CMSG_DATA

CMSG_DATA

CMSG_LEN

CMSG_LEN

CMSG_SPACE

CMSG_SPACE

IOV_MAX

NGX_HAVE_DEBUG_MALLOC

NGX_HAVE_INHERITED_NONBLOCK

NGX_HAVE_OS_SPECIFIC_INIT

NGX_KEEPALIVE_FACTOR

NGX_LISTEN_BACKLOG

_NGX_FREEBSD_CONFIG_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_

‌#define _NGX_FREEBSD_CONFIG_H_INCLUDED_

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>

#include <stdarg.h>

#include <stddef.h>             /* offsetof() */

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <pwd.h>

#include <grp.h>

#include <dirent.h>

#include <glob.h>

#include <time.h>

#include <sys/param.h>          /* ALIGN() */

#include <sys/mount.h>          /* statfs() */

#include <sys/filio.h>          /* FIONBIO */

#include <sys/uio.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/mman.h>

#include <sys/resource.h>

#include <sched.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>        /* TCP_NODELAY, TCP_NOPUSH */

#include <arpa/inet.h>

#include <netdb.h>

#include <sys/un.h>

#include <libutil.h>            /* setproctitle() before 4.1 */

#include <osreldate.h>

#include <sys/sysctl.h>

#if __FreeBSD_version < 400017

/*

* FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()

*/

‌#undef  CMSG_SPACE

‌#define CMSG_SPACE(l)       (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))

‌#undef  CMSG_LEN

‌#define CMSG_LEN(l)         (ALIGN(sizeof(struct cmsghdr)) + (l))

‌#undef  CMSG_DATA

‌#define CMSG_DATA(cmsg)     ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))

#endif

#include <ngx_auto_config.h>

#if (NGX_HAVE_POSIX_SEM)

#include <semaphore.h>

#endif

#if (NGX_HAVE_POLL)

#include <poll.h>

#endif

#if (NGX_HAVE_KQUEUE)

#include <sys/event.h>

#endif

#if (NGX_HAVE_FILE_AIO || NGX_HAVE_AIO)

#include <aio.h>

‌typedef struct aiocb  ngx_aiocb_t;

#endif

‌#define NGX_LISTEN_BACKLOG        -1

#ifdef __DragonFly__

‌#define NGX_KEEPALIVE_FACTOR      1000

#endif

#if (__FreeBSD_version < 430000 || __FreeBSD_version < 500012)

pid_t rfork_thread(int flags, void *stack, int (*func)(void *arg), void *arg);

#endif

#ifndef IOV_MAX

‌#define IOV_MAX   1024

#endif

#ifndef NGX_HAVE_INHERITED_NONBLOCK

‌#define NGX_HAVE_INHERITED_NONBLOCK  1

#endif

‌#define NGX_HAVE_OS_SPECIFIC_INIT    1

‌#define NGX_HAVE_DEBUG_MALLOC        1

extern char **environ;

extern char  *malloc_options;

#endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */

src/event/modules/ngx_eventport_module.c - nginx-1.7.9

Global variables defined

ep

event_list

event_timer

eventport_name

nevents

ngx_eventport_commands

ngx_eventport_module

ngx_eventport_module_ctx

Data types defined

clockid_t

itimerspec

itimerspec_t

ngx_eventport_conf_t

port_event_t

port_notify

port_notify_t

timer_t

Functions defined

ngx_eventport_add_event

ngx_eventport_create_conf

ngx_eventport_del_event

ngx_eventport_done

ngx_eventport_init

ngx_eventport_init_conf

ngx_eventport_process_events

port_associate

port_create

port_dissociate

port_getn

timer_create

timer_delete

timer_settime

Macros defined

CLOCK_REALTIME

ETIME

PORT_SOURCE_AIO

PORT_SOURCE_ALERT

PORT_SOURCE_FD

PORT_SOURCE_MQ

PORT_SOURCE_TIMER

PORT_SOURCE_USER

SIGEV_PORT

uint_t

ushort_t

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_TEST_BUILD_EVENTPORT)

‌#define ushort_t  u_short

‌#define uint_t    u_int

#ifndef CLOCK_REALTIME

‌#define CLOCK_REALTIME          0

‌typedef int     clockid_t;

‌typedef void *  timer_t;

#endif

/* Solaris declarations */

‌#define PORT_SOURCE_AIO         1

‌#define PORT_SOURCE_TIMER       2

‌#define PORT_SOURCE_USER        3

‌#define PORT_SOURCE_FD          4

‌#define PORT_SOURCE_ALERT       5

‌#define PORT_SOURCE_MQ          6

#ifndef ETIME

‌#define ETIME                   64

#endif

‌#define SIGEV_PORT              4

typedef struct {

    int         portev_events;  /* event data is source specific */

    ushort_t    portev_source;  /* event source */

    ushort_t    portev_pad;     /* port internal use */

    uintptr_t   portev_object;  /* source specific object */

    void       *portev_user;    /* user cookie */

‌} port_event_t;

‌typedef struct  port_notify {

    int         portnfy_port;   /* bind request(s) to port */

    void       *portnfy_user;   /* user defined */

‌} port_notify_t;

#if (__FreeBSD_version < 700005)

‌typedef struct itimerspec {     /* definition per POSIX.4 */

    struct timespec it_interval;/* timer period */

    struct timespec it_value;   /* timer expiration */

‌} itimerspec_t;

#endif

int port_create(void);

‌int port_create(void)

{

    return -1;

}

int port_associate(int port, int source, uintptr_t object, int events,

    void *user);

‌int port_associate(int port, int source, uintptr_t object, int events,

    void *user)

{

    return -1;

}

int port_dissociate(int port, int source, uintptr_t object);

‌int port_dissociate(int port, int source, uintptr_t object)

{

    return -1;

}

int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,

    struct timespec *timeout);

‌int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,

    struct timespec *timeout)

{

    return -1;

}

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);

‌int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

{

    return -1;

}

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,

    struct itimerspec *ovalue);

‌int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,

    struct itimerspec *ovalue)

{

    return -1;

}

int timer_delete(timer_t timerid);

‌int timer_delete(timer_t timerid)

{

    return -1;

}

#endif

typedef struct {

    ngx_uint_t  events;

‌} ngx_eventport_conf_t;

static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_eventport_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle,

    ngx_msec_t timer, ngx_uint_t flags);

static void *ngx_eventport_create_conf(ngx_cycle_t *cycle);

static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf);

‌static int            ep = -1;

‌static port_event_t  *event_list;

‌static ngx_uint_t     nevents;

‌static timer_t        event_timer = (timer_t) -1;

‌static ngx_str_t      eventport_name = ngx_string("eventport");

‌static ngx_command_t  ngx_eventport_commands[] = {

    { ngx_string("eventport_events"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_eventport_conf_t, events),

      NULL },

      ngx_null_command

};

‌ngx_event_module_t  ngx_eventport_module_ctx = {

    &eventport_name,

    ngx_eventport_create_conf,             /* create configuration */

    ngx_eventport_init_conf,               /* init configuration */

    {

        ngx_eventport_add_event,           /* add an event */

        ngx_eventport_del_event,           /* delete an event */

        ngx_eventport_add_event,           /* enable an event */

        ngx_eventport_del_event,           /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        NULL,                              /* process the changes */

        ngx_eventport_process_events,      /* process the events */

        ngx_eventport_init,                /* init the events */

        ngx_eventport_done,                /* done the events */

    }

};

‌ngx_module_t  ngx_eventport_module = {

    NGX_MODULE_V1,

    &ngx_eventport_module_ctx,             /* module context */

    ngx_eventport_commands,                /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    port_notify_t          pn;

    struct itimerspec      its;

    struct sigevent        sev;

    ngx_eventport_conf_t  *epcf;

    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module);

    if (ep == -1) {

        ep = port_create();

        if (ep == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "port_create() failed");

            return NGX_ERROR;

        }

    }

    if (nevents < epcf->events) {

        if (event_list) {

            ngx_free(event_list);

        }

        event_list = ngx_alloc(sizeof(port_event_t) * epcf->events,

                               cycle->log);

        if (event_list == NULL) {

            return NGX_ERROR;

        }

    }

    ngx_event_flags = NGX_USE_EVENTPORT_EVENT;

    if (timer) {

        ngx_memzero(&pn, sizeof(port_notify_t));

        pn.portnfy_port = ep;

        ngx_memzero(&sev, sizeof(struct sigevent));

        sev.sigev_notify = SIGEV_PORT;

#if !(NGX_TEST_BUILD_EVENTPORT)

        sev.sigev_value.sival_ptr = &pn;

#endif

        if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "timer_create() failed");

            return NGX_ERROR;

        }

        its.it_interval.tv_sec = timer / 1000;

        its.it_interval.tv_nsec = (timer % 1000) * 1000000;

        its.it_value.tv_sec = timer / 1000;

        its.it_value.tv_nsec = (timer % 1000) * 1000000;

        if (timer_settime(event_timer, 0, &its, NULL) == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "timer_settime() failed");

            return NGX_ERROR;

        }

        ngx_event_flags |= NGX_USE_TIMER_EVENT;

    }

    nevents = epcf->events;

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_eventport_module_ctx.actions;

    return NGX_OK;

}

static void

‌ngx_eventport_done(ngx_cycle_t *cycle)

{

    if (event_timer != (timer_t) -1) {

        if (timer_delete(event_timer) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "timer_delete() failed");

        }

        event_timer = (timer_t) -1;

    }

    if (close(ep) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "close() event port failed");

    }

    ep = -1;

    ngx_free(event_list);

    event_list = NULL;

    nevents = 0;

}

static ngx_int_t

‌ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_int_t          events, prev;

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

    events = event;

    if (event == NGX_READ_EVENT) {

        e = c->write;

        prev = POLLOUT;

#if (NGX_READ_EVENT != POLLIN)

        events = POLLIN;

#endif

    } else {

        e = c->read;

        prev = POLLIN;

#if (NGX_WRITE_EVENT != POLLOUT)

        events = POLLOUT;

#endif

    }

    if (e->oneshot) {

        events |= prev;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "eventport add event: fd:%d ev:%04Xi", c->fd, events);

    if (port_associate(ep, PORT_SOURCE_FD, c->fd, events,

                       (void *) ((uintptr_t) ev | ev->instance))

        == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                      "port_associate() failed");

        return NGX_ERROR;

    }

    ev->active = 1;

    ev->oneshot = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    /*

     * when the file descriptor is closed, the event port automatically

     * dissociates it from the port, so we do not need to dissociate explicitly

     * the event before the closing the file descriptor

     */

    if (flags & NGX_CLOSE_EVENT) {

        ev->active = 0;

        ev->oneshot = 0;

        return NGX_OK;

    }

    c = ev->data;

    if (event == NGX_READ_EVENT) {

        e = c->write;

        event = POLLOUT;

    } else {

        e = c->read;

        event = POLLIN;

    }

    if (e->oneshot) {

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "eventport change event: fd:%d ev:%04Xi", c->fd, event);

        if (port_associate(ep, PORT_SOURCE_FD, c->fd, event,

                           (void *) ((uintptr_t) ev | ev->instance))

            == -1)

        {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                          "port_associate() failed");

            return NGX_ERROR;

        }

    } else {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "eventport del event: fd:%d", c->fd);

        if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                          "port_dissociate() failed");

            return NGX_ERROR;

        }

    }

    ev->active = 0;

    ev->oneshot = 0;

    return NGX_OK;

}

ngx_int_t

‌ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int                 n, revents;

    u_int               events;

    ngx_err_t           err;

    ngx_int_t           instance;

    ngx_uint_t          i, level;

    ngx_event_t        *ev, *rev, *wev;

    ngx_queue_t        *queue;

    ngx_connection_t   *c;

    struct timespec     ts, *tp;

    if (timer == NGX_TIMER_INFINITE) {

        tp = NULL;

    } else {

        ts.tv_sec = timer / 1000;

        ts.tv_nsec = (timer % 1000) * 1000000;

        tp = &ts;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "eventport timer: %M", timer);

    events = 1;

    n = port_getn(ep, event_list, (u_int) nevents, &events, tp);

    err = ngx_errno;

    if (flags & NGX_UPDATE_TIME) {

        ngx_time_update();

    }

    if (n == -1) {

        if (err == ETIME) {

            if (timer != NGX_TIMER_INFINITE) {

                return NGX_OK;

            }

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "port_getn() returned no events without timeout");

            return NGX_ERROR;

        }

        level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT;

        ngx_log_error(level, cycle->log, err, "port_getn() failed");

        return NGX_ERROR;

    }

    if (events == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "port_getn() returned no events without timeout");

        return NGX_ERROR;

    }

    for (i = 0; i < events; i++) {

        if (event_list[i].portev_source == PORT_SOURCE_TIMER) {

            ngx_time_update();

            continue;

        }

        ev = event_list[i].portev_user;

        switch (event_list[i].portev_source) {

        case PORT_SOURCE_FD:

            instance = (uintptr_t) ev & 1;

            ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);

            if (ev->closed || ev->instance != instance) {

                /*

                 * the stale event from a file descriptor

                 * that was just closed in this iteration

                 */

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "eventport: stale event %p", ev);

                continue;

            }

            revents = event_list[i].portev_events;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "eventport: fd:%d, ev:%04Xd",

                           event_list[i].portev_object, revents);

            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {

                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "port_getn() error fd:%d ev:%04Xd",

                               event_list[i].portev_object, revents);

            }

            if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                              "strange port_getn() events fd:%d ev:%04Xd",

                              event_list[i].portev_object, revents);

            }

            if ((revents & (POLLERR|POLLHUP|POLLNVAL))

                 && (revents & (POLLIN|POLLOUT)) == 0)

            {

                /*

                 * if the error events were returned without POLLIN or POLLOUT,

                 * then add these flags to handle the events at least in one

                 * active handler

                 */

                revents |= POLLIN|POLLOUT;

            }

            c = ev->data;

            rev = c->read;

            wev = c->write;

            rev->active = 0;

            wev->active = 0;

            if (revents & POLLIN) {

                rev->ready = 1;

                if (flags & NGX_POST_EVENTS) {

                    queue = rev->accept ? &ngx_posted_accept_events

                                        : &ngx_posted_events;

                    ngx_post_event(rev, queue);

                } else {

                    rev->handler(rev);

                    if (ev->closed || ev->instance != instance) {

                        continue;

                    }

                }

                if (rev->accept) {

                    if (ngx_use_accept_mutex) {

                        ngx_accept_events = 1;

                        continue;

                    }

                    if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN,

                                       (void *) ((uintptr_t) ev | ev->instance))

                        == -1)

                    {

                        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                                      "port_associate() failed");

                        return NGX_ERROR;

                    }

                }

            }

            if (revents & POLLOUT) {

                wev->ready = 1;

                if (flags & NGX_POST_EVENTS) {

                    ngx_post_event(wev, &ngx_posted_events);

                } else {

                    wev->handler(wev);

                }

            }

            continue;

        default:

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "unexpected even_port object %d",

                          event_list[i].portev_object);

            continue;

        }

    }

    return NGX_OK;

}

static void *

‌ngx_eventport_create_conf(ngx_cycle_t *cycle)

{

    ngx_eventport_conf_t  *epcf;

    epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));

    if (epcf == NULL) {

        return NULL;

    }

    epcf->events = NGX_CONF_UNSET;

    return epcf;

}

static char *

‌ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_eventport_conf_t *epcf = conf;

    ngx_conf_init_uint_value(epcf->events, 32);

    return NGX_CONF_OK;

}

src/event/modules/ngx_devpoll_module.c - nginx-1.7.9

Global variables defined

change_index

change_list

devpoll_name

dp

event_list

max_changes

nchanges

nevents

ngx_devpoll_commands

ngx_devpoll_module

ngx_devpoll_module_ctx

Data types defined

dvpoll

ngx_devpoll_conf_t

Functions defined

ngx_devpoll_add_event

ngx_devpoll_create_conf

ngx_devpoll_del_event

ngx_devpoll_done

ngx_devpoll_init

ngx_devpoll_init_conf

ngx_devpoll_process_events

ngx_devpoll_set_event

Macros defined

DP_ISPOLLED

DP_POLL

POLLREMOVE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_TEST_BUILD_DEVPOLL)

/* Solaris declarations */

‌#define POLLREMOVE   0x0800

‌#define DP_POLL      0xD001

‌#define DP_ISPOLLED  0xD002

‌struct dvpoll {

    struct pollfd  *dp_fds;

    int             dp_nfds;

    int             dp_timeout;

};

#endif

typedef struct {

    ngx_uint_t      changes;

    ngx_uint_t      events;

‌} ngx_devpoll_conf_t;

static ngx_int_t ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_devpoll_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle,

    ngx_msec_t timer, ngx_uint_t flags);

static void *ngx_devpoll_create_conf(ngx_cycle_t *cycle);

static char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf);

‌static int              dp = -1;

‌‌static struct pollfd   *change_list, *event_list;

‌‌‌static ngx_uint_t       nchanges, max_changes, nevents;

‌static ngx_event_t    **change_index;

‌static ngx_str_t      devpoll_name = ngx_string("/dev/poll");

‌static ngx_command_t  ngx_devpoll_commands[] = {

    { ngx_string("devpoll_changes"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_devpoll_conf_t, changes),

      NULL },

    { ngx_string("devpoll_events"),

      NGX_EVENT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      0,

      offsetof(ngx_devpoll_conf_t, events),

      NULL },

      ngx_null_command

};

‌ngx_event_module_t  ngx_devpoll_module_ctx = {

    &devpoll_name,

    ngx_devpoll_create_conf,               /* create configuration */

    ngx_devpoll_init_conf,                 /* init configuration */

    {

        ngx_devpoll_add_event,             /* add an event */

        ngx_devpoll_del_event,             /* delete an event */

        ngx_devpoll_add_event,             /* enable an event */

        ngx_devpoll_del_event,             /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        NULL,                              /* process the changes */

        ngx_devpoll_process_events,        /* process the events */

        ngx_devpoll_init,                  /* init the events */

        ngx_devpoll_done,                  /* done the events */

    }

};

‌ngx_module_t  ngx_devpoll_module = {

    NGX_MODULE_V1,

    &ngx_devpoll_module_ctx,               /* module context */

    ngx_devpoll_commands,                  /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    size_t               n;

    ngx_devpoll_conf_t  *dpcf;

    dpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_devpoll_module);

    if (dp == -1) {

        dp = open("/dev/poll", O_RDWR);

        if (dp == -1) {

            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

                          "open(/dev/poll) failed");

            return NGX_ERROR;

        }

    }

    if (max_changes < dpcf->changes) {

        if (nchanges) {

            n = nchanges * sizeof(struct pollfd);

            if (write(dp, change_list, n) != (ssize_t) n) {

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                              "write(/dev/poll) failed");

                return NGX_ERROR;

            }

            nchanges = 0;

        }

        if (change_list) {

            ngx_free(change_list);

        }

        change_list = ngx_alloc(sizeof(struct pollfd) * dpcf->changes,

                                cycle->log);

        if (change_list == NULL) {

            return NGX_ERROR;

        }

        if (change_index) {

            ngx_free(change_index);

        }

        change_index = ngx_alloc(sizeof(ngx_event_t *) * dpcf->changes,

                                 cycle->log);

        if (change_index == NULL) {

            return NGX_ERROR;

        }

    }

    max_changes = dpcf->changes;

    if (nevents < dpcf->events) {

        if (event_list) {

            ngx_free(event_list);

        }

        event_list = ngx_alloc(sizeof(struct pollfd) * dpcf->events,

                               cycle->log);

        if (event_list == NULL) {

            return NGX_ERROR;

        }

    }

    nevents = dpcf->events;

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_devpoll_module_ctx.actions;

    ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;

    return NGX_OK;

}

static void

‌ngx_devpoll_done(ngx_cycle_t *cycle)

{

    if (close(dp) == -1) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                      "close(/dev/poll) failed");

    }

    dp = -1;

    ngx_free(change_list);

    ngx_free(event_list);

    ngx_free(change_index);

    change_list = NULL;

    event_list = NULL;

    change_index = NULL;

    max_changes = 0;

    nchanges = 0;

    nevents = 0;

}

static ngx_int_t

‌ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

#if (NGX_DEBUG)

    ngx_connection_t *c;

#endif

#if (NGX_READ_EVENT != POLLIN)

    event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;

#endif

#if (NGX_DEBUG)

    c = ev->data;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "devpoll add event: fd:%d ev:%04Xi", c->fd, event);

#endif

    ev->active = 1;

    return ngx_devpoll_set_event(ev, event, 0);

}

static ngx_int_t

‌ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

#if (NGX_READ_EVENT != POLLIN)

    event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;

#endif

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "devpoll del event: fd:%d ev:%04Xi", c->fd, event);

    if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) {

        return NGX_ERROR;

    }

    ev->active = 0;

    if (flags & NGX_CLOSE_EVENT) {

        e = (event == POLLIN) ? c->write : c->read;

        if (e) {

            e->active = 0;

        }

        return NGX_OK;

    }

    /* restore the pair event if it exists */

    if (event == POLLIN) {

        e = c->write;

        event = POLLOUT;

    } else {

        e = c->read;

        event = POLLIN;

    }

    if (e && e->active) {

        return ngx_devpoll_set_event(e, event, 0);

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    size_t             n;

    ngx_connection_t  *c;

    c = ev->data;

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "devpoll fd:%d ev:%04Xi fl:%04Xi", c->fd, event, flags);

    if (nchanges >= max_changes) {

        ngx_log_error(NGX_LOG_WARN, ev->log, 0,

                      "/dev/pool change list is filled up");

        n = nchanges * sizeof(struct pollfd);

        if (write(dp, change_list, n) != (ssize_t) n) {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                          "write(/dev/poll) failed");

            return NGX_ERROR;

        }

        nchanges = 0;

    }

    change_list[nchanges].fd = c->fd;

    change_list[nchanges].events = (short) event;

    change_list[nchanges].revents = 0;

    change_index[nchanges] = ev;

    ev->index = nchanges;

    nchanges++;

    if (flags & NGX_CLOSE_EVENT) {

        n = nchanges * sizeof(struct pollfd);

        if (write(dp, change_list, n) != (ssize_t) n) {

            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

                          "write(/dev/poll) failed");

            return NGX_ERROR;

        }

        nchanges = 0;

    }

    return NGX_OK;

}

ngx_int_t

‌ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int                 events, revents, rc;

    size_t              n;

    ngx_fd_t            fd;

    ngx_err_t           err;

    ngx_int_t           i;

    ngx_uint_t          level, instance;

    ngx_event_t        *rev, *wev;

    ngx_queue_t        *queue;

    ngx_connection_t   *c;

    struct pollfd       pfd;

    struct dvpoll       dvp;

    /* NGX_TIMER_INFINITE == INFTIM */

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "devpoll timer: %M", timer);

    if (nchanges) {

        n = nchanges * sizeof(struct pollfd);

        if (write(dp, change_list, n) != (ssize_t) n) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "write(/dev/poll) failed");

            return NGX_ERROR;

        }

        nchanges = 0;

    }

    dvp.dp_fds = event_list;

    dvp.dp_nfds = (int) nevents;

    dvp.dp_timeout = timer;

    events = ioctl(dp, DP_POLL, &dvp);

    err = (events == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

        ngx_time_update();

    }

    if (err) {

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {

                ngx_event_timer_alarm = 0;

                return NGX_OK;

            }

            level = NGX_LOG_INFO;

        } else {

            level = NGX_LOG_ALERT;

        }

        ngx_log_error(level, cycle->log, err, "ioctl(DP_POLL) failed");

        return NGX_ERROR;

    }

    if (events == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "ioctl(DP_POLL) returned no events without timeout");

        return NGX_ERROR;

    }

    for (i = 0; i < events; i++) {

        fd = event_list[i].fd;

        revents = event_list[i].revents;

        c = ngx_cycle->files[fd];

        if (c == NULL || c->fd == -1) {

            pfd.fd = fd;

            pfd.events = 0;

            pfd.revents = 0;

            rc = ioctl(dp, DP_ISPOLLED, &pfd);

            switch (rc) {

            case -1:

                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                    "ioctl(DP_ISPOLLED) failed for socket %d, event %04Xd",

                    fd, revents);

                break;

            case 0:

                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                    "phantom event %04Xd for closed and removed socket %d",

                    revents, fd);

                break;

            default:

                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                    "unexpected event %04Xd for closed and removed socket %d, ",

                    "ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd",

                    revents, fd, rc, pfd.fd, pfd.revents);

                pfd.fd = fd;

                pfd.events = POLLREMOVE;

                pfd.revents = 0;

                if (write(dp, &pfd, sizeof(struct pollfd))

                    != (ssize_t) sizeof(struct pollfd))

                {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                                  "write(/dev/poll) for %d failed", fd);

                }

                if (close(fd) == -1) {

                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                                  "close(%d) failed", fd);

                }

                break;

            }

            continue;

        }

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "devpoll: fd:%d, ev:%04Xd, rev:%04Xd",

                       fd, event_list[i].events, revents);

        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                          "ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd",

                          fd, event_list[i].events, revents);

        }

        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "strange ioctl(DP_POLL) events "

                          "fd:%d ev:%04Xd rev:%04Xd",

                          fd, event_list[i].events, revents);

        }

        if ((revents & (POLLERR|POLLHUP|POLLNVAL))

             && (revents & (POLLIN|POLLOUT)) == 0)

        {

            /*

             * if the error events were returned without POLLIN or POLLOUT,

             * then add these flags to handle the events at least in one

             * active handler

             */

            revents |= POLLIN|POLLOUT;

        }

        rev = c->read;

        if ((revents & POLLIN) && rev->active) {

            rev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                queue = rev->accept ? &ngx_posted_accept_events

                                    : &ngx_posted_events;

                ngx_post_event(rev, queue);

            } else {

                instance = rev->instance;

                rev->handler(rev);

                if (c->fd == -1 || rev->instance != instance) {

                    continue;

                }

            }

        }

        wev = c->write;

        if ((revents & POLLOUT) && wev->active) {

            wev->ready = 1;

            if (flags & NGX_POST_EVENTS) {

                ngx_post_event(wev, &ngx_posted_events);

            } else {

                wev->handler(wev);

            }

        }

    }

    return NGX_OK;

}

static void *

‌ngx_devpoll_create_conf(ngx_cycle_t *cycle)

{

    ngx_devpoll_conf_t  *dpcf;

    dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t));

    if (dpcf == NULL) {

        return NULL;

    }

    dpcf->changes = NGX_CONF_UNSET;

    dpcf->events = NGX_CONF_UNSET;

    return dpcf;

}

static char *

‌ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_devpoll_conf_t *dpcf = conf;

    ngx_conf_init_uint_value(dpcf->changes, 32);

    ngx_conf_init_uint_value(dpcf->events, 32);

    return NGX_CONF_OK;

}

src/event/modules/ngx_select_module.c - nginx-1.7.9

Global variables defined

event_index

master_read_fd_set

master_write_fd_set

max_fd

nevents

ngx_select_module

ngx_select_module_ctx

select_name

work_read_fd_set

work_write_fd_set

Functions defined

ngx_select_add_event

ngx_select_del_event

ngx_select_done

ngx_select_init

ngx_select_init_conf

ngx_select_process_events

ngx_select_repair_fd_sets

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_select_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);

static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);

‌static fd_set         master_read_fd_set;

‌static fd_set         master_write_fd_set;

‌static fd_set         work_read_fd_set;

‌static fd_set         work_write_fd_set;

‌static ngx_int_t      max_fd;

‌static ngx_uint_t     nevents;

‌static ngx_event_t  **event_index;

‌static ngx_str_t    select_name = ngx_string("select");

‌ngx_event_module_t  ngx_select_module_ctx = {

    &select_name,

    NULL,                                  /* create configuration */

    ngx_select_init_conf,                  /* init configuration */

    {

        ngx_select_add_event,              /* add an event */

        ngx_select_del_event,              /* delete an event */

        ngx_select_add_event,              /* enable an event */

        ngx_select_del_event,              /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        NULL,                              /* process the changes */

        ngx_select_process_events,         /* process the events */

        ngx_select_init,                   /* init the events */

        ngx_select_done                    /* done the events */

    }

};

‌ngx_module_t  ngx_select_module = {

    NGX_MODULE_V1,

    &ngx_select_module_ctx,                /* module context */

    NULL,                                  /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    ngx_event_t  **index;

    if (event_index == NULL) {

        FD_ZERO(&master_read_fd_set);

        FD_ZERO(&master_write_fd_set);

        nevents = 0;

    }

    if (ngx_process >= NGX_PROCESS_WORKER

        || cycle->old_cycle == NULL

        || cycle->old_cycle->connection_n < cycle->connection_n)

    {

        index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,

                          cycle->log);

        if (index == NULL) {

            return NGX_ERROR;

        }

        if (event_index) {

            ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);

            ngx_free(event_index);

        }

        event_index = index;

    }

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_select_module_ctx.actions;

    ngx_event_flags = NGX_USE_LEVEL_EVENT;

    max_fd = -1;

    return NGX_OK;

}

static void

‌ngx_select_done(ngx_cycle_t *cycle)

{

    ngx_free(event_index);

    event_index = NULL;

}

static ngx_int_t

‌ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_connection_t  *c;

    c = ev->data;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "select add event fd:%d ev:%i", c->fd, event);

    if (ev->index != NGX_INVALID_INDEX) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "select event fd:%d ev:%i is already set", c->fd, event);

        return NGX_OK;

    }

    if ((event == NGX_READ_EVENT && ev->write)

        || (event == NGX_WRITE_EVENT && !ev->write))

    {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "invalid select %s event fd:%d ev:%i",

                      ev->write ? "write" : "read", c->fd, event);

        return NGX_ERROR;

    }

    if (event == NGX_READ_EVENT) {

        FD_SET(c->fd, &master_read_fd_set);

    } else if (event == NGX_WRITE_EVENT) {

        FD_SET(c->fd, &master_write_fd_set);

    }

    if (max_fd != -1 && max_fd < c->fd) {

        max_fd = c->fd;

    }

    ev->active = 1;

    event_index[nevents] = ev;

    ev->index = nevents;

    nevents++;

    return NGX_OK;

}

static ngx_int_t

‌ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

    ev->active = 0;

    if (ev->index == NGX_INVALID_INDEX) {

        return NGX_OK;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "select del event fd:%d ev:%i", c->fd, event);

    if (event == NGX_READ_EVENT) {

        FD_CLR(c->fd, &master_read_fd_set);

    } else if (event == NGX_WRITE_EVENT) {

        FD_CLR(c->fd, &master_write_fd_set);

    }

    if (max_fd == c->fd) {

        max_fd = -1;

    }

    if (ev->index < --nevents) {

        e = event_index[nevents];

        event_index[ev->index] = e;

        e->index = ev->index;

    }

    ev->index = NGX_INVALID_INDEX;

    return NGX_OK;

}

static ngx_int_t

‌ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int                ready, nready;

    ngx_err_t          err;

    ngx_uint_t         i, found;

    ngx_event_t       *ev;

    ngx_queue_t       *queue;

    struct timeval     tv, *tp;

    ngx_connection_t  *c;

    if (max_fd == -1) {

        for (i = 0; i < nevents; i++) {

            c = event_index[i]->data;

            if (max_fd < c->fd) {

                max_fd = c->fd;

            }

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "change max_fd: %i", max_fd);

    }

#if (NGX_DEBUG)

    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {

        for (i = 0; i < nevents; i++) {

            ev = event_index[i];

            c = ev->data;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "select event: fd:%d wr:%d", c->fd, ev->write);

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "max_fd: %i", max_fd);

    }

#endif

    if (timer == NGX_TIMER_INFINITE) {

        tp = NULL;

    } else {

        tv.tv_sec = (long) (timer / 1000);

        tv.tv_usec = (long) ((timer % 1000) * 1000);

        tp = &tv;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "select timer: %M", timer);

    work_read_fd_set = master_read_fd_set;

    work_write_fd_set = master_write_fd_set;

    ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);

    err = (ready == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

        ngx_time_update();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "select ready %d", ready);

    if (err) {

        ngx_uint_t  level;

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {

                ngx_event_timer_alarm = 0;

                return NGX_OK;

            }

            level = NGX_LOG_INFO;

        } else {

            level = NGX_LOG_ALERT;

        }

        ngx_log_error(level, cycle->log, err, "select() failed");

        if (err == NGX_EBADF) {

            ngx_select_repair_fd_sets(cycle);

        }

        return NGX_ERROR;

    }

    if (ready == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "select() returned no events without timeout");

        return NGX_ERROR;

    }

    nready = 0;

    for (i = 0; i < nevents; i++) {

        ev = event_index[i];

        c = ev->data;

        found = 0;

        if (ev->write) {

            if (FD_ISSET(c->fd, &work_write_fd_set)) {

                found = 1;

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "select write %d", c->fd);

            }

        } else {

            if (FD_ISSET(c->fd, &work_read_fd_set)) {

                found = 1;

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "select read %d", c->fd);

            }

        }

        if (found) {

            ev->ready = 1;

            queue = ev->accept ? &ngx_posted_accept_events

                               : &ngx_posted_events;

            ngx_post_event(ev, queue);

            nready++;

        }

    }

    if (ready != nready) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "select ready != events: %d:%d", ready, nready);

        ngx_select_repair_fd_sets(cycle);

    }

    return NGX_OK;

}

static void

‌ngx_select_repair_fd_sets(ngx_cycle_t *cycle)

{

    int           n;

    socklen_t     len;

    ngx_err_t     err;

    ngx_socket_t  s;

    for (s = 0; s <= max_fd; s++) {

        if (FD_ISSET(s, &master_read_fd_set) == 0) {

            continue;

        }

        len = sizeof(int);

        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {

            err = ngx_socket_errno;

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "invalid descriptor #%d in read fd_set", s);

            FD_CLR(s, &master_read_fd_set);

        }

    }

    for (s = 0; s <= max_fd; s++) {

        if (FD_ISSET(s, &master_write_fd_set) == 0) {

            continue;

        }

        len = sizeof(int);

        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {

            err = ngx_socket_errno;

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "invalid descriptor #%d in write fd_set", s);

            FD_CLR(s, &master_write_fd_set);

        }

    }

    max_fd = -1;

}

static char *

‌ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_event_conf_t  *ecf;

    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ecf->use != ngx_select_module.ctx_index) {

        return NGX_CONF_OK;

    }

    /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */

    if (cycle->connection_n > FD_SETSIZE) {

        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                      "the maximum number of files "

                      "supported by select() is %ud", FD_SETSIZE);

        return NGX_CONF_ERROR;

    }

#if (NGX_THREADS)

    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                  "select() is not supported in the threaded mode");

    return NGX_CONF_ERROR;

#else

    return NGX_CONF_OK;

#endif

}

src/os/unix/ngx_readv_chain.c - nginx-1.7.9

Functions defined

ngx_readv_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

ssize_t

‌ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit)

{

    u_char        *prev;

    ssize_t        n, size;

    ngx_err_t      err;

    ngx_array_t    vec;

    ngx_event_t   *rev;

    struct iovec  *iov, iovs[NGX_IOVS_PREALLOCATE];

    rev = c->read;

#if (NGX_HAVE_KQUEUE)

    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "readv: eof:%d, avail:%d, err:%d",

                       rev->pending_eof, rev->available, rev->kq_errno);

        if (rev->available == 0) {

            if (rev->pending_eof) {

                rev->ready = 0;

                rev->eof = 1;

                ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,

                              "kevent() reported about an closed connection");

                if (rev->kq_errno) {

                    rev->error = 1;

                    ngx_set_socket_errno(rev->kq_errno);

                    return NGX_ERROR;

                }

                return 0;

            } else {

                return NGX_AGAIN;

            }

        }

    }

#endif

    prev = NULL;

    iov = NULL;

    size = 0;

    vec.elts = iovs;

    vec.nelts = 0;

    vec.size = sizeof(struct iovec);

    vec.nalloc = NGX_IOVS_PREALLOCATE;

    vec.pool = c->pool;

    /* coalesce the neighbouring bufs */

    while (chain) {

        n = chain->buf->end - chain->buf->last;

        if (limit) {

            if (size >= limit) {

                break;

            }

            if (size + n > limit) {

                n = (ssize_t) (limit - size);

            }

        }

        if (prev == chain->buf->last) {

            iov->iov_len += n;

        } else {

            if (vec.nelts >= IOV_MAX) {

                break;

            }

            iov = ngx_array_push(&vec);

            if (iov == NULL) {

                return NGX_ERROR;

            }

            iov->iov_base = (void *) chain->buf->last;

            iov->iov_len = n;

        }

        size += n;

        prev = chain->buf->end;

        chain = chain->next;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "readv: %d, last:%d", vec.nelts, iov->iov_len);

    do {

        n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);

        if (n >= 0) {

#if (NGX_HAVE_KQUEUE)

            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

                rev->available -= n;

                /*

                 * rev->available may be negative here because some additional

                 * bytes may be received between kevent() and recv()

                 */

                if (rev->available <= 0) {

                    if (!rev->pending_eof) {

                        rev->ready = 0;

                    }

                    if (rev->available < 0) {

                        rev->available = 0;

                    }

                }

                if (n == 0) {

                    /*

                     * on FreeBSD recv() may return 0 on closed socket

                     * even if kqueue reported about available data

                     */

#if 0

                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                                  "readv() returned 0 while kevent() reported "

                                  "%d available bytes", rev->available);

#endif

                    rev->ready = 0;

                    rev->eof = 1;

                    rev->available = 0;

                }

                return n;

            }

#endif /* NGX_HAVE_KQUEUE */

            if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {

                rev->ready = 0;

            }

            if (n == 0) {

                rev->eof = 1;

            }

            return n;

        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "readv() not ready");

            n = NGX_AGAIN;

        } else {

            n = ngx_connection_error(c, err, "readv() failed");

            break;

        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {

        c->read->error = 1;

    }

    return n;

}

src/os/unix/ngx_udp_recv.c - nginx-1.7.9

Functions defined

ngx_udp_unix_recv

ngx_udp_unix_recv

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

#if (NGX_HAVE_KQUEUE)

ssize_t

‌ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *rev;

    rev = c->read;

    do {

        n = recv(c->fd, buf, size, 0);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n >= 0) {

            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {

                rev->available -= n;

                /*

                 * rev->available may be negative here because some additional

                 * bytes may be received between kevent() and recv()

                 */

                if (rev->available <= 0) {

                    rev->ready = 0;

                    rev->available = 0;

                }

            }

            return n;

        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "recv() not ready");

            n = NGX_AGAIN;

        } else {

            n = ngx_connection_error(c, err, "recv() failed");

            break;

        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {

        rev->error = 1;

    }

    return n;

}

#else /* ! NGX_HAVE_KQUEUE */

ssize_t

‌ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *rev;

    rev = c->read;

    do {

        n = recv(c->fd, buf, size, 0);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "recv: fd:%d %d of %d", c->fd, n, size);

        if (n >= 0) {

            return n;

        }

        err = ngx_socket_errno;

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "recv() not ready");

            n = NGX_AGAIN;

        } else {

            n = ngx_connection_error(c, err, "recv() failed");

            break;

        }

    } while (err == NGX_EINTR);

    rev->ready = 0;

    if (n == NGX_ERROR) {

        rev->error = 1;

    }

    return n;

}

#endif /* NGX_HAVE_KQUEUE */

src/os/unix/ngx_send.c - nginx-1.7.9

Functions defined

ngx_unix_send

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

ssize_t

‌ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size)

{

    ssize_t       n;

    ngx_err_t     err;

    ngx_event_t  *wev;

    wev = c->write;

#if (NGX_HAVE_KQUEUE)

    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {

        (void) ngx_connection_error(c, wev->kq_errno,

                               "kevent() reported about an closed connection");

        wev->error = 1;

        return NGX_ERROR;

    }

#endif

    for ( ;; ) {

        n = send(c->fd, buf, size, 0);

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "send: fd:%d %d of %d", c->fd, n, size);

        if (n > 0) {

            if (n < (ssize_t) size) {

                wev->ready = 0;

            }

            c->sent += n;

            return n;

        }

        err = ngx_socket_errno;

        if (n == 0) {

            ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero");

            wev->ready = 0;

            return n;

        }

        if (err == NGX_EAGAIN || err == NGX_EINTR) {

            wev->ready = 0;

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                           "send() not ready");

            if (err == NGX_EAGAIN) {

                return NGX_AGAIN;

            }

        } else {

            wev->error = 1;

            (void) ngx_connection_error(c, err, "send() failed");

            return NGX_ERROR;

        }

    }

}

src/os/unix/ngx_freebsd_sendfile_chain.c - nginx-1.7.9

Functions defined

ngx_freebsd_sendfile_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

/*

* Although FreeBSD sendfile() allows to pass a header and a trailer,

* it cannot send a header with a part of the file in one packet until

* FreeBSD 5.3.  Besides, over the fast ethernet connection sendfile()

* may send the partially filled packets, i.e. the 8 file pages may be sent

* as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,

* and then again the 11 full 1460-bytes packets.

*

* Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)

* to postpone the sending - it not only sends a header and the first part of

* the file in one packet, but also sends the file pages in the full packets.

*

* But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending

* data that less than MSS, so that data may be sent with 5 second delay.

* So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used

* for non-keepalive HTTP connections.

*/

ngx_chain_t *

‌ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    int              rc, flags;

    off_t            send, prev_send, sent;

    size_t           file_size;

    ssize_t          n;

    ngx_uint_t       eintr, eagain;

    ngx_err_t        err;

    ngx_buf_t       *file;

    ngx_event_t     *wev;

    ngx_chain_t     *cl;

    ngx_iovec_t      header, trailer;

    struct sf_hdtr   hdtr;

    struct iovec     headers[NGX_IOVS_PREALLOCATE];

    struct iovec     trailers[NGX_IOVS_PREALLOCATE];

    wev = c->write;

    if (!wev->ready) {

        return in;

    }

#if (NGX_HAVE_KQUEUE)

    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {

        (void) ngx_connection_error(c, wev->kq_errno,

                               "kevent() reported about an closed connection");

        wev->error = 1;

        return NGX_CHAIN_ERROR;

    }

#endif

    /* the maximum limit size is the maximum size_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;

    }

    send = 0;

    eagain = 0;

    flags = 0;

    header.iovs = headers;

    header.nalloc = NGX_IOVS_PREALLOCATE;

    trailer.iovs = trailers;

    trailer.nalloc = NGX_IOVS_PREALLOCATE;

    for ( ;; ) {

        eintr = 0;

        prev_send = send;

        /* create the header iovec and coalesce the neighbouring bufs */

        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);

        if (cl == NGX_CHAIN_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        send += header.size;

        if (cl && cl->buf->in_file && send < limit) {

            file = cl->buf;

            /* coalesce the neighbouring file bufs */

            file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);

            send += file_size;

            /* create the trailer iovec and coalesce the neighbouring bufs */

            cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, c->log);

            if (cl == NGX_CHAIN_ERROR) {

                return NGX_CHAIN_ERROR;

            }

            send += trailer.size;

            if (ngx_freebsd_use_tcp_nopush

                && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)

            {

                if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {

                    err = ngx_socket_errno;

                    /*

                     * there is a tiny chance to be interrupted, however,

                     * we continue a processing without the TCP_NOPUSH

                     */

                    if (err != NGX_EINTR) {

                        wev->error = 1;

                        (void) ngx_connection_error(c, err,

                                                    ngx_tcp_nopush_n " failed");

                        return NGX_CHAIN_ERROR;

                    }

                } else {

                    c->tcp_nopush = NGX_TCP_NOPUSH_SET;

                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                                   "tcp_nopush");

                }

            }

            /*

             * sendfile() does unneeded work if sf_hdtr's count is 0,

             * but corresponding pointer is not NULL

             */

            hdtr.headers = header.count ? header.iovs : NULL;

            hdtr.hdr_cnt = header.count;

            hdtr.trailers = trailer.count ? trailer.iovs : NULL;

            hdtr.trl_cnt = trailer.count;

            /*

             * the "nbytes bug" of the old sendfile() syscall:

             * http://bugs.freebsd.org/33771

             */

            if (!ngx_freebsd_sendfile_nbytes_bug) {

                header.size = 0;

            }

            sent = 0;

#if (NGX_HAVE_AIO_SENDFILE)

            flags = c->aio_sendfile ? SF_NODISKIO : 0;

#endif

            rc = sendfile(file->file->fd, c->fd, file->file_pos,

                          file_size + header.size, &hdtr, &sent, flags);

            if (rc == -1) {

                err = ngx_errno;

                switch (err) {

                case NGX_EAGAIN:

                    eagain = 1;

                    break;

                case NGX_EINTR:

                    eintr = 1;

                    break;

#if (NGX_HAVE_AIO_SENDFILE)

                case NGX_EBUSY:

                    c->busy_sendfile = file;

                    break;

#endif

                default:

                    wev->error = 1;

                    (void) ngx_connection_error(c, err, "sendfile() failed");

                    return NGX_CHAIN_ERROR;

                }

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,

                               "sendfile() sent only %O bytes", sent);

            /*

             * sendfile() in FreeBSD 3.x-4.x may return value >= 0

             * on success, although only 0 is documented

             */

            } else if (rc >= 0 && sent == 0) {

                /*

                 * if rc is OK and sent equal to zero, then someone

                 * has truncated the file, so the offset became beyond

                 * the end of the file

                 */

                ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                         "sendfile() reported that \"%s\" was truncated at %O",

                         file->file->name.data, file->file_pos);

                return NGX_CHAIN_ERROR;

            }

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "sendfile: %d, @%O %O:%uz",

                           rc, file->file_pos, sent, file_size + header.size);

        } else {

            n = ngx_writev(c, &header);

            if (n == NGX_ERROR) {

                return NGX_CHAIN_ERROR;

            }

            sent = (n == NGX_AGAIN) ? 0 : n;

        }

        c->sent += sent;

        in = ngx_chain_update_sent(in, sent);

#if (NGX_HAVE_AIO_SENDFILE)

        if (c->busy_sendfile) {

            return in;

        }

#endif

        if (eagain) {

            /*

             * sendfile() may return EAGAIN, even if it has sent a whole file

             * part, it indicates that the successive sendfile() call would

             * return EAGAIN right away and would not send anything.

             * We use it as a hint.

             */

            wev->ready = 0;

            return in;

        }

        if (eintr) {

            send = prev_send + sent;

            continue;

        }

        if (send - prev_send != sent) {

            wev->ready = 0;

            return in;

        }

        if (send >= limit || in == NULL) {

            return in;

        }

    }

}

src/os/unix/ngx_daemon.c - nginx-1.7.9

Functions defined

ngx_daemon

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

ngx_int_t

‌ngx_daemon(ngx_log_t *log)

{

    int  fd;

    switch (fork()) {

    case -1:

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");

        return NGX_ERROR;

    case 0:

        break;

    default:

        exit(0);

    }

    ngx_pid = ngx_getpid();

    if (setsid() == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");

        return NGX_ERROR;

    }

    umask(0);

    fd = open("/dev/null", O_RDWR);

    if (fd == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,

                      "open(\"/dev/null\") failed");

        return NGX_ERROR;

    }

    if (dup2(fd, STDIN_FILENO) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");

        return NGX_ERROR;

    }

    if (dup2(fd, STDOUT_FILENO) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");

        return NGX_ERROR;

    }

#if 0

    if (dup2(fd, STDERR_FILENO) == -1) {

        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");

        return NGX_ERROR;

    }

#endif

    if (fd > STDERR_FILENO) {

        if (close(fd) == -1) {

            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

src/os/unix/ngx_aio_read.c - nginx-1.7.9

Functions defined

ngx_aio_read

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

extern int  ngx_kqueue;

ssize_t

‌ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size)

{

    int           n;

    ngx_event_t  *rev;

    rev = c->read;

    if (!rev->ready) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second aio post");

        return NGX_AGAIN;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "rev->complete: %d", rev->complete);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                   "aio size: %d", size);

    if (!rev->complete) {

        ngx_memzero(&rev->aiocb, sizeof(struct aiocb));

        rev->aiocb.aio_fildes = c->fd;

        rev->aiocb.aio_buf = buf;

        rev->aiocb.aio_nbytes = size;

#if (NGX_HAVE_KQUEUE)

        rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;

        rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;

        rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev;

#endif

        if (aio_read(&rev->aiocb) == -1) {

            ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno,

                          "aio_read() failed");

            rev->error = 1;

            return NGX_ERROR;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "aio_read: #%d OK", c->fd);

        rev->active = 1;

        rev->ready = 0;

    }

    rev->complete = 0;

    n = aio_error(&rev->aiocb);

    if (n == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "aio_error() failed");

        rev->error = 1;

        return NGX_ERROR;

    }

    if (n != 0) {

        if (n == NGX_EINPROGRESS) {

            if (rev->ready) {

                ngx_log_error(NGX_LOG_ALERT, c->log, n,

                              "aio_read() still in progress");

                rev->ready = 0;

            }

            return NGX_AGAIN;

        }

        ngx_log_error(NGX_LOG_CRIT, c->log, n, "aio_read() failed");

        rev->error = 1;

        rev->ready = 0;

        return NGX_ERROR;

    }

    n = aio_return(&rev->aiocb);

    if (n == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "aio_return() failed");

        rev->error = 1;

        rev->ready = 0;

        return NGX_ERROR;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0,

                   "aio_read: #%d %d", c->fd, n);

    if (n == 0) {

        rev->eof = 1;

        rev->ready = 0;

    } else {

        rev->ready = 1;

    }

    rev->active = 0;

    return n;

}

src/os/unix/ngx_aio_read_chain.c - nginx-1.7.9

Functions defined

ngx_aio_read_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

ssize_t

‌ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)

{

    int           n;

    u_char       *buf, *prev;

    size_t        size;

    ssize_t       total;

    if (c->read->pending_eof) {

        c->read->ready = 0;

        return 0;

    }

    total = 0;

    while (cl) {

        /* we can post the single aio operation only */

        if (!c->read->ready) {

            return total ? total : NGX_AGAIN;

        }

        buf = cl->buf->last;

        prev = cl->buf->last;

        size = 0;

        /* coalesce the neighbouring bufs */

        while (cl && prev == cl->buf->last) {

            size += cl->buf->end - cl->buf->last;

            prev = cl->buf->end;

            cl = cl->next;

        }

        n = ngx_aio_read(c, buf, size);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_read: %d", n);

        if (n == NGX_AGAIN) {

            return total ? total : NGX_AGAIN;

        }

        if (n == NGX_ERROR) {

            return NGX_ERROR;

        }

        if (n == 0) {

            c->read->pending_eof = 1;

            if (total) {

                c->read->eof = 0;

                c->read->ready = 1;

            }

            return total;

        }

        if (n > 0) {

            total += n;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "aio_read total: %d", total);

    }

    return total ? total : NGX_AGAIN;

}

src/os/unix/ngx_aio_write.c - nginx-1.7.9

Functions defined

ngx_aio_write

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

extern int  ngx_kqueue;

ssize_t

‌ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size)

{

    int           n;

    ngx_event_t  *wev;

    wev = c->write;

    if (!wev->ready) {

        return NGX_AGAIN;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0,

                   "aio: wev->complete: %d", wev->complete);

    if (!wev->complete) {

        ngx_memzero(&wev->aiocb, sizeof(struct aiocb));

        wev->aiocb.aio_fildes = c->fd;

        wev->aiocb.aio_buf = buf;

        wev->aiocb.aio_nbytes = size;

#if (NGX_HAVE_KQUEUE)

        wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;

        wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;

        wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev;

#endif

        if (aio_write(&wev->aiocb) == -1) {

            ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno,

                          "aio_write() failed");

            return NGX_ERROR;

        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: OK");

        wev->active = 1;

        wev->ready = 0;

    }

    wev->complete = 0;

    n = aio_error(&wev->aiocb);

    if (n == -1) {

        ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, "aio_error() failed");

        wev->error = 1;

        return NGX_ERROR;

    }

    if (n != 0) {

        if (n == NGX_EINPROGRESS) {

            if (wev->ready) {

                ngx_log_error(NGX_LOG_ALERT, wev->log, n,

                              "aio_write() still in progress");

                wev->ready = 0;

            }

            return NGX_AGAIN;

        }

        ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_write() failed");

        wev->error = 1;

        wev->ready = 0;

#if 1

        n = aio_return(&wev->aiocb);

        if (n == -1) {

            ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,

                          "aio_return() failed");

        }

        ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_return() %d", n);

#endif

        return NGX_ERROR;

    }

    n = aio_return(&wev->aiocb);

    if (n == -1) {

        ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,

                      "aio_return() failed");

        wev->error = 1;

        wev->ready = 0;

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: %d", n);

    wev->active = 0;

    wev->ready = 1;

    return n;

}

src/os/unix/ngx_aio_write_chain.c - nginx-1.7.9

Functions defined

ngx_aio_write_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

ngx_chain_t *

‌ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    u_char       *buf, *prev;

    off_t         send, sent;

    size_t        len;

    ssize_t       n, size;

    ngx_chain_t  *cl;

    /* the maximum limit size is the maximum size_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;

    }

    send = 0;

    sent = 0;

    cl = in;

    while (cl) {

        if (cl->buf->pos == cl->buf->last) {

            cl = cl->next;

            continue;

        }

        /* we can post the single aio operation only */

        if (!c->write->ready) {

            return cl;

        }

        buf = cl->buf->pos;

        prev = buf;

        len = 0;

        /* coalesce the neighbouring bufs */

        while (cl && prev == cl->buf->pos && send < limit) {

            if (ngx_buf_special(cl->buf)) {

                continue;

            }

            size = cl->buf->last - cl->buf->pos;

            if (send + size > limit) {

                size = limit - send;

            }

            len += size;

            prev = cl->buf->pos + size;

            send += size;

            cl = cl->next;

        }

        n = ngx_aio_write(c, buf, len);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_write: %z", n);

        if (n == NGX_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        if (n > 0) {

            sent += n;

            c->sent += n;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,

                       "aio_write sent: %O", c->sent);

        for (cl = in; cl; cl = cl->next) {

            if (sent >= cl->buf->last - cl->buf->pos) {

                sent -= cl->buf->last - cl->buf->pos;

                cl->buf->pos = cl->buf->last;

                continue;

            }

            cl->buf->pos += sent;

            break;

        }

    }

    return cl;

}

src/core/ngx_cpuinfo.c - nginx-1.7.9

Functions defined

ngx_cpuid

ngx_cpuid

ngx_cpuinfo

ngx_cpuinfo

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (( __i386__ || __amd64__ ) && ( __GNUC__ || __INTEL_COMPILER ))

static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);

#if ( __i386__ )

static ngx_inline void

‌ngx_cpuid(uint32_t i, uint32_t *buf)

{

    /*

     * we could not use %ebx as output parameter if gcc builds PIC,

     * and we could not save %ebx on stack, because %esp is used,

     * when the -fomit-frame-pointer optimization is specified.

     */

    __asm__ (

    "    mov    %%ebx, %%esi;  "

    "    cpuid;                "

    "    mov    %%eax, (%1);   "

    "    mov    %%ebx, 4(%1);  "

    "    mov    %%edx, 8(%1);  "

    "    mov    %%ecx, 12(%1); "

    "    mov    %%esi, %%ebx;  "

    : : "a" (i), "D" (buf) : "ecx", "edx", "esi", "memory" );

}

#else /* __amd64__ */

static ngx_inline void

‌ngx_cpuid(uint32_t i, uint32_t *buf)

{

    uint32_t  eax, ebx, ecx, edx;

    __asm__ (

        "cpuid"

    : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (i) );

    buf[0] = eax;

    buf[1] = ebx;

    buf[2] = edx;

    buf[3] = ecx;

}

#endif

/* auto detect the L2 cache line size of modern and widespread CPUs */

void

‌ngx_cpuinfo(void)

{

    u_char    *vendor;

    uint32_t   vbuf[5], cpu[4], model;

    vbuf[0] = 0;

    vbuf[1] = 0;

    vbuf[2] = 0;

    vbuf[3] = 0;

    vbuf[4] = 0;

    ngx_cpuid(0, vbuf);

    vendor = (u_char *) &vbuf[1];

    if (vbuf[0] == 0) {

        return;

    }

    ngx_cpuid(1, cpu);

    if (ngx_strcmp(vendor, "GenuineIntel") == 0) {

        switch ((cpu[0] & 0xf00) >> 8) {

        /* Pentium */

        case 5:

            ngx_cacheline_size = 32;

            break;

        /* Pentium Pro, II, III */

        case 6:

            ngx_cacheline_size = 32;

            model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0);

            if (model >= 0xd0) {

                /* Intel Core, Core 2, Atom */

                ngx_cacheline_size = 64;

            }

            break;

        /*

         * Pentium 4, although its cache line size is 64 bytes,

         * it prefetches up to two cache lines during memory read

         */

        case 15:

            ngx_cacheline_size = 128;

            break;

        }

    } else if (ngx_strcmp(vendor, "AuthenticAMD") == 0) {

        ngx_cacheline_size = 64;

    }

}

#else

void

‌ngx_cpuinfo(void)

{

}

#endif

src/core/ngx_syslog.h - nginx-1.7.9

Data types defined

ngx_syslog_peer_t

Macros defined

_NGX_SYSLOG_H_INCLUDED_

Source code

/*

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SYSLOG_H_INCLUDED_

‌#define _NGX_SYSLOG_H_INCLUDED_

typedef struct {

    ngx_pool_t       *pool;

    ngx_uint_t        facility;

    ngx_uint_t        severity;

    ngx_str_t         tag;

    ngx_addr_t        server;

    ngx_connection_t  conn;

    ngx_uint_t        busy;  /* unsigned busy:1; */

‌} ngx_syslog_peer_t;

char *ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer);

u_char *ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf);

void ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,

    size_t len);

ssize_t ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len);

#endif /* _NGX_SYSLOG_H_INCLUDED_ */

src/core/ngx_syslog.c - nginx-1.7.9

Global variables defined

facilities

ngx_syslog_dummy_event

ngx_syslog_dummy_log

severities

Functions defined

ngx_syslog_add_header

ngx_syslog_cleanup

ngx_syslog_init_peer

ngx_syslog_parse_args

ngx_syslog_process_conf

ngx_syslog_send

ngx_syslog_writer

Macros defined

NGX_SYSLOG_MAX_STR

Source code

/*

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌#define NGX_SYSLOG_MAX_STR                                                    \

     NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1                  \

     + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                               \

     + 32 /* tag */ + 2 /* colon, space */

static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);

static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);

static void ngx_syslog_cleanup(void *data);

‌static char  *facilities[] = {

    "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",

    "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",

    "local1", "local2", "local3", "local4", "local5", "local6", "local7",

    NULL

};

/* note 'error/warn' like in nginx.conf, not 'err/warning' */

‌static char  *severities[] = {

    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL

};

‌static ngx_log_t    ngx_syslog_dummy_log;

‌static ngx_event_t  ngx_syslog_dummy_event;

char *

‌ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)

{

    peer->pool = cf->pool;

    peer->facility = NGX_CONF_UNSET_UINT;

    peer->severity = NGX_CONF_UNSET_UINT;

    if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {

        return NGX_CONF_ERROR;

    }

    if (peer->server.sockaddr == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "no syslog server specified");

        return NGX_CONF_ERROR;

    }

    if (peer->facility == NGX_CONF_UNSET_UINT) {

        peer->facility = 23; /* local7 */

    }

    if (peer->severity == NGX_CONF_UNSET_UINT) {

        peer->severity = 6; /* info */

    }

    if (peer->tag.data == NULL) {

        ngx_str_set(&peer->tag, "nginx");

    }

    peer->conn.fd = (ngx_socket_t) -1;

    return NGX_CONF_OK;

}

static char *

‌ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)

{

    u_char      *p, *comma, c;

    size_t       len;

    ngx_str_t   *value;

    ngx_url_t    u;

    ngx_uint_t   i;

    value = cf->args->elts;

    p = value[1].data + sizeof("syslog:") - 1;

    for ( ;; ) {

        comma = (u_char *) ngx_strchr(p, ',');

        if (comma != NULL) {

            len = comma - p;

            *comma = '\0';

        } else {

            len = value[1].data + value[1].len - p;

        }

        if (ngx_strncmp(p, "server=", 7) == 0) {

            if (peer->server.sockaddr != NULL) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "duplicate syslog \"server\"");

                return NGX_CONF_ERROR;

            }

            ngx_memzero(&u, sizeof(ngx_url_t));

            u.url.data = p + 7;

            u.url.len = len - 7;

            u.default_port = 514;

            if (ngx_parse_url(cf->pool, &u) != NGX_OK) {

                if (u.err) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "%s in syslog server \"%V\"",

                                       u.err, &u.url);

                }

                return NGX_CONF_ERROR;

            }

            peer->server = u.addrs[0];

        } else if (ngx_strncmp(p, "facility=", 9) == 0) {

            if (peer->facility != NGX_CONF_UNSET_UINT) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "duplicate syslog \"facility\"");

                return NGX_CONF_ERROR;

            }

            for (i = 0; facilities[i] != NULL; i++) {

                if (ngx_strcmp(p + 9, facilities[i]) == 0) {

                    peer->facility = i;

                    goto next;

                }

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "unknown syslog facility \"%s\"", p + 9);

            return NGX_CONF_ERROR;

        } else if (ngx_strncmp(p, "severity=", 9) == 0) {

            if (peer->severity != NGX_CONF_UNSET_UINT) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "duplicate syslog \"severity\"");

                return NGX_CONF_ERROR;

            }

            for (i = 0; severities[i] != NULL; i++) {

                if (ngx_strcmp(p + 9, severities[i]) == 0) {

                    peer->severity = i;

                    goto next;

                }

            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "unknown syslog severity \"%s\"", p + 9);

            return NGX_CONF_ERROR;

        } else if (ngx_strncmp(p, "tag=", 4) == 0) {

            if (peer->tag.data != NULL) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "duplicate syslog \"tag\"");

                return NGX_CONF_ERROR;

            }

            /*

             * RFC 3164: the TAG is a string of ABNF alphanumeric characters

             * that MUST NOT exceed 32 characters.

             */

            if (len - 4 > 32) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "syslog tag length exceeds 32");

                return NGX_CONF_ERROR;

            }

            for (i = 4; i < len; i++) {

                c = ngx_tolower(p[i]);

                if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "syslog \"tag\" only allows "

                                       "alphanumeric characters "

                                       "and underscore");

                    return NGX_CONF_ERROR;

                }

            }

            peer->tag.data = p + 4;

            peer->tag.len = len - 4;

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "unknown syslog parameter \"%s\"", p);

            return NGX_CONF_ERROR;

        }

    next:

        if (comma == NULL) {

            break;

        }

        p = comma + 1;

    }

    return NGX_CONF_OK;

}

u_char *

‌ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)

{

    ngx_uint_t  pri;

    pri = peer->facility * 8 + peer->severity;

    return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,

                       &ngx_cycle->hostname, &peer->tag);

}

void

‌ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,

    size_t len)

{

    u_char             *p, msg[NGX_SYSLOG_MAX_STR];

    ngx_uint_t          head_len;

    ngx_syslog_peer_t  *peer;

    peer = log->wdata;

    if (peer->busy) {

        return;

    }

    peer->busy = 1;

    peer->severity = level - 1;

    p = ngx_syslog_add_header(peer, msg);

    head_len = p - msg;

    len -= NGX_LINEFEED_SIZE;

    if (len > NGX_SYSLOG_MAX_STR - head_len) {

        len = NGX_SYSLOG_MAX_STR - head_len;

    }

    p = ngx_snprintf(p, len, "%s", buf);

    (void) ngx_syslog_send(peer, msg, p - msg);

    peer->busy = 0;

}

ssize_t

‌ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)

{

    ssize_t  n;

    if (peer->conn.fd == (ngx_socket_t) -1) {

        if (ngx_syslog_init_peer(peer) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    /* log syslog socket events with valid log */

    peer->conn.log = ngx_cycle->log;

    if (ngx_send) {

        n = ngx_send(&peer->conn, buf, len);

    } else {

        /* event module has not yet set ngx_io */

        n = ngx_os_io.send(&peer->conn, buf, len);

    }

#if (NGX_HAVE_UNIX_DOMAIN)

    if (n == NGX_ERROR && peer->server.sockaddr->sa_family == AF_UNIX) {

        if (ngx_close_socket(peer->conn.fd) == -1) {

            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                          ngx_close_socket_n " failed");

        }

        peer->conn.fd = (ngx_socket_t) -1;

    }

#endif

    return n;

}

static ngx_int_t

‌ngx_syslog_init_peer(ngx_syslog_peer_t *peer)

{

    ngx_socket_t         fd;

    ngx_pool_cleanup_t  *cln;

    peer->conn.read = &ngx_syslog_dummy_event;

    peer->conn.write = &ngx_syslog_dummy_event;

    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;

    fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);

    if (fd == (ngx_socket_t) -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                      ngx_socket_n " failed");

        return NGX_ERROR;

    }

    if (ngx_nonblocking(fd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                      ngx_nonblocking_n " failed");

        goto failed;

    }

    if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                      "connect() failed");

        goto failed;

    }

    cln = ngx_pool_cleanup_add(peer->pool, 0);

    if (cln == NULL) {

        goto failed;

    }

    cln->data = peer;

    cln->handler = ngx_syslog_cleanup;

    peer->conn.fd = fd;

    /* UDP sockets are always ready to write */

    peer->conn.write->ready = 1;

    return NGX_OK;

failed:

    if (ngx_close_socket(fd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                      ngx_close_socket_n " failed");

    }

    return NGX_ERROR;

}

static void

‌ngx_syslog_cleanup(void *data)

{

    ngx_syslog_peer_t  *peer = data;

    /* prevents further use of this peer */

    peer->busy = 1;

    if (peer->conn.fd == (ngx_socket_t) -1) {

        return;

    }

    if (ngx_close_socket(peer->conn.fd) == -1) {

        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,

                      ngx_close_socket_n " failed");

    }

}

src/event/ngx_event_busy_lock.c - nginx-1.7.9

Functions defined

ngx_event_busy_lock

ngx_event_busy_lock_cacheable

ngx_event_busy_lock_cancel

ngx_event_busy_lock_handler

ngx_event_busy_lock_look_cacheable

ngx_event_busy_lock_posted_handler

ngx_event_busy_unlock

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static ngx_int_t ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx);

static void ngx_event_busy_lock_handler(ngx_event_t *ev);

static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev);

/*

* NGX_OK:     the busy lock is held

* NGX_AGAIN:  the all busy locks are held but we will wait the specified time

* NGX_BUSY:   ctx->timer == 0: there are many the busy locks

*             ctx->timer != 0: there are many the waiting locks

*/

ngx_int_t

‌ngx_event_busy_lock(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx)

{

    ngx_int_t  rc;

    ngx_mutex_lock(bl->mutex);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,

                   "event busy lock: b:%d mb:%d",

                   bl->busy, bl->max_busy);

    if (bl->busy < bl->max_busy) {

        bl->busy++;

        rc = NGX_OK;

    } else if (ctx->timer && bl->waiting < bl->max_waiting) {

        bl->waiting++;

        ngx_add_timer(ctx->event, ctx->timer);

        ctx->event->handler = ngx_event_busy_lock_handler;

        if (bl->events) {

            bl->last->next = ctx;

        } else {

            bl->events = ctx;

        }

        bl->last = ctx;

        rc = NGX_AGAIN;

    } else {

        rc = NGX_BUSY;

    }

    ngx_mutex_unlock(bl->mutex);

    return rc;

}

ngx_int_t

‌ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx)

{

    ngx_int_t  rc;

    ngx_mutex_lock(bl->mutex);

    rc = ngx_event_busy_lock_look_cacheable(bl, ctx);

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,

                   "event busy lock: %d w:%d mw:%d",

                   rc, bl->waiting, bl->max_waiting);

    /*

     * NGX_OK:     no the same request, there is free slot and we locked it

     * NGX_BUSY:   no the same request and there is no free slot

     * NGX_AGAIN:  the same request is processing

     */

    if (rc == NGX_AGAIN) {

        if (ctx->timer && bl->waiting < bl->max_waiting) {

            bl->waiting++;

            ngx_add_timer(ctx->event, ctx->timer);

            ctx->event->handler = ngx_event_busy_lock_handler;

            if (bl->events == NULL) {

                bl->events = ctx;

            } else {

                bl->last->next = ctx;

            }

            bl->last = ctx;

        } else {

            rc = NGX_BUSY;

        }

    }

    ngx_mutex_unlock(bl->mutex);

    return rc;

}

void

‌ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx)

{

    ngx_event_t                *ev;

    ngx_event_busy_lock_ctx_t  *wakeup;

    ngx_mutex_lock(bl->mutex);

    if (bl->events) {

        wakeup = bl->events;

        bl->events = bl->events->next;

    } else {

        wakeup = NULL;

        bl->busy--;

    }

    /*

     * MP: all ctx's and their queue must be in shared memory,

     *     each ctx has pid to wake up

     */

    if (wakeup == NULL) {

        ngx_mutex_unlock(bl->mutex);

        return;

    }

    if (ctx->md5) {

        for (wakeup = bl->events; wakeup; wakeup = wakeup->next) {

            if (wakeup->md5 == NULL || wakeup->slot != ctx->slot) {

                continue;

            }

            wakeup->handler = ngx_event_busy_lock_posted_handler;

            wakeup->cache_updated = 1;

            ev = wakeup->event;

            ngx_post_event(ev, &ngx_posted_events);

        }

        ngx_mutex_unlock(bl->mutex);

    } else {

        bl->waiting--;

        ngx_mutex_unlock(bl->mutex);

        wakeup->handler = ngx_event_busy_lock_posted_handler;

        wakeup->locked = 1;

        ev = wakeup->event;

        if (ev->timer_set) {

            ngx_del_timer(ev);

        }

        ngx_post_event(ev, &ngx_posted_events);

    }

}

void

‌ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx)

{

    ngx_event_busy_lock_ctx_t  *c, *p;

    ngx_mutex_lock(bl->mutex);

    bl->waiting--;

    if (ctx == bl->events) {

        bl->events = ctx->next;

    } else {

        p = bl->events;

        for (c = bl->events->next; c; c = c->next) {

            if (c == ctx) {

                p->next = ctx->next;

                break;

            }

            p = c;

        }

    }

    ngx_mutex_unlock(bl->mutex);

}

static ngx_int_t

‌ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx)

{

    ngx_int_t    free;

    ngx_uint_t   i, bit, cacheable, mask;

    bit = 0;

    cacheable = 0;

    free = -1;

#if (NGX_SUPPRESS_WARN)

    mask = 0;

#endif

    for (i = 0; i < bl->max_busy; i++) {

        if ((bit & 7) == 0) {

            mask = bl->md5_mask[i / 8];

        }

        if (mask & 1) {

            if (ngx_memcmp(&bl->md5[i * 16], ctx->md5, 16) == 0) {

                ctx->waiting = 1;

                ctx->slot = i;

                return NGX_AGAIN;

            }

            cacheable++;

        } else if (free == -1) {

            free = i;

        }

        if (cacheable == bl->cacheable) {

            if (free == -1 && cacheable < bl->max_busy) {

                free = i + 1;

            }

            break;

        }

        mask >>= 1;

        bit++;

    }

    if (free == -1) {

        return NGX_BUSY;

    }

#if 0

    if (bl->busy == bl->max_busy) {

        return NGX_BUSY;

    }

#endif

    ngx_memcpy(&bl->md5[free * 16], ctx->md5, 16);

    bl->md5_mask[free / 8] |= 1 << (free & 7);

    ctx->slot = free;

    bl->cacheable++;

    bl->busy++;

    return NGX_OK;

}

static void

‌ngx_event_busy_lock_handler(ngx_event_t *ev)

{

    ev->handler = ngx_event_busy_lock_posted_handler;

    ngx_post_event(ev, &ngx_posted_events);

}

static void

‌ngx_event_busy_lock_posted_handler(ngx_event_t *ev)

{

    ngx_event_busy_lock_ctx_t  *ctx;

    ctx = ev->data;

    ctx->handler(ev);

}

src/event/ngx_event_busy_lock.h - nginx-1.7.9

Data types defined

ngx_event_busy_lock_ctx_s

ngx_event_busy_lock_ctx_t

ngx_event_busy_lock_t

Macros defined

_NGX_EVENT_BUSY_LOCK_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_EVENT_BUSY_LOCK_H_INCLUDED_

‌#define _NGX_EVENT_BUSY_LOCK_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌typedef struct ngx_event_busy_lock_ctx_s  ngx_event_busy_lock_ctx_t;

‌struct ngx_event_busy_lock_ctx_s {

    ngx_event_t                *event;

    ngx_event_handler_pt        handler;

    void                       *data;

    ngx_msec_t                  timer;

    unsigned                    locked:1;

    unsigned                    waiting:1;

    unsigned                    cache_updated:1;

    char                       *md5;

    ngx_int_t                   slot;

    ngx_event_busy_lock_ctx_t  *next;

};

typedef struct {

    u_char                     *md5_mask;

    char                       *md5;

    ngx_uint_t                  cacheable;

    ngx_uint_t                  busy;

    ngx_uint_t                  max_busy;

    ngx_uint_t                  waiting;

    ngx_uint_t                  max_waiting;

    ngx_event_busy_lock_ctx_t  *events;

    ngx_event_busy_lock_ctx_t  *last;

#if (NGX_THREADS)

    ngx_mutex_t                *mutex;

#endif

‌} ngx_event_busy_lock_t;

ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx);

ngx_int_t ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx);

void ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx);

void ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,

    ngx_event_busy_lock_ctx_t *ctx);

#endif /* _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ */

src/event/modules/ngx_poll_module.c - nginx-1.7.9

Global variables defined

event_list

nevents

ngx_poll_module

ngx_poll_module_ctx

poll_name

Functions defined

ngx_poll_add_event

ngx_poll_del_event

ngx_poll_done

ngx_poll_init

ngx_poll_init_conf

ngx_poll_process_events

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_poll_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);

‌static struct pollfd  *event_list;

‌static ngx_uint_t      nevents;

‌static ngx_str_t    poll_name = ngx_string("poll");

‌ngx_event_module_t  ngx_poll_module_ctx = {

    &poll_name,

    NULL,                                  /* create configuration */

    ngx_poll_init_conf,                    /* init configuration */

    {

        ngx_poll_add_event,                /* add an event */

        ngx_poll_del_event,                /* delete an event */

        ngx_poll_add_event,                /* enable an event */

        ngx_poll_del_event,                /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        NULL,                              /* process the changes */

        ngx_poll_process_events,           /* process the events */

        ngx_poll_init,                     /* init the events */

        ngx_poll_done                      /* done the events */

    }

};

‌ngx_module_t  ngx_poll_module = {

    NGX_MODULE_V1,

    &ngx_poll_module_ctx,                  /* module context */

    NULL,                                  /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    struct pollfd   *list;

    if (event_list == NULL) {

        nevents = 0;

    }

    if (ngx_process >= NGX_PROCESS_WORKER

        || cycle->old_cycle == NULL

        || cycle->old_cycle->connection_n < cycle->connection_n)

    {

        list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,

                         cycle->log);

        if (list == NULL) {

            return NGX_ERROR;

        }

        if (event_list) {

            ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents);

            ngx_free(event_list);

        }

        event_list = list;

    }

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_poll_module_ctx.actions;

    ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;

    return NGX_OK;

}

static void

‌ngx_poll_done(ngx_cycle_t *cycle)

{

    ngx_free(event_list);

    event_list = NULL;

}

static ngx_int_t

‌ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

    ev->active = 1;

    if (ev->index != NGX_INVALID_INDEX) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "poll event fd:%d ev:%i is already set", c->fd, event);

        return NGX_OK;

    }

    if (event == NGX_READ_EVENT) {

        e = c->write;

#if (NGX_READ_EVENT != POLLIN)

        event = POLLIN;

#endif

    } else {

        e = c->read;

#if (NGX_WRITE_EVENT != POLLOUT)

        event = POLLOUT;

#endif

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "poll add event: fd:%d ev:%i", c->fd, event);

    if (e == NULL || e->index == NGX_INVALID_INDEX) {

        event_list[nevents].fd = c->fd;

        event_list[nevents].events = (short) event;

        event_list[nevents].revents = 0;

        ev->index = nevents;

        nevents++;

    } else {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "poll add index: %i", e->index);

        event_list[e->index].events |= (short) event;

        ev->index = e->index;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

    ev->active = 0;

    if (ev->index == NGX_INVALID_INDEX) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "poll event fd:%d ev:%i is already deleted",

                      c->fd, event);

        return NGX_OK;

    }

    if (event == NGX_READ_EVENT) {

        e = c->write;

#if (NGX_READ_EVENT != POLLIN)

        event = POLLIN;

#endif

    } else {

        e = c->read;

#if (NGX_WRITE_EVENT != POLLOUT)

        event = POLLOUT;

#endif

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "poll del event: fd:%d ev:%i", c->fd, event);

    if (e == NULL || e->index == NGX_INVALID_INDEX) {

        nevents--;

        if (ev->index < nevents) {

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                           "index: copy event %ui to %i", nevents, ev->index);

            event_list[ev->index] = event_list[nevents];

            c = ngx_cycle->files[event_list[nevents].fd];

            if (c->fd == -1) {

                ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                              "unexpected last event");

            } else {

                if (c->read->index == nevents) {

                    c->read->index = ev->index;

                }

                if (c->write->index == nevents) {

                    c->write->index = ev->index;

                }

            }

        }

    } else {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                       "poll del index: %i", e->index);

        event_list[e->index].events &= (short) ~event;

    }

    ev->index = NGX_INVALID_INDEX;

    return NGX_OK;

}

static ngx_int_t

‌ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)

{

    int                 ready, revents;

    ngx_err_t           err;

    ngx_uint_t          i, found, level;

    ngx_event_t        *ev;

    ngx_queue_t        *queue;

    ngx_connection_t   *c;

    /* NGX_TIMER_INFINITE == INFTIM */

#if (NGX_DEBUG0)

    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {

        for (i = 0; i < nevents; i++) {

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "poll: %ui: fd:%d ev:%04Xd",

                           i, event_list[i].fd, event_list[i].events);

        }

    }

#endif

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer);

    ready = poll(event_list, (u_int) nevents, (int) timer);

    err = (ready == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

        ngx_time_update();

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "poll ready %d of %ui", ready, nevents);

    if (err) {

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {

                ngx_event_timer_alarm = 0;

                return NGX_OK;

            }

            level = NGX_LOG_INFO;

        } else {

            level = NGX_LOG_ALERT;

        }

        ngx_log_error(level, cycle->log, err, "poll() failed");

        return NGX_ERROR;

    }

    if (ready == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "poll() returned no events without timeout");

        return NGX_ERROR;

    }

    for (i = 0; i < nevents && ready; i++) {

        revents = event_list[i].revents;

#if 1

        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                       "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",

                       i, event_list[i].fd, event_list[i].events, revents);

#else

        if (revents) {

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",

                           i, event_list[i].fd, event_list[i].events, revents);

        }

#endif

        if (revents & POLLNVAL) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "poll() error fd:%d ev:%04Xd rev:%04Xd",

                          event_list[i].fd, event_list[i].events, revents);

        }

        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                          "strange poll() events fd:%d ev:%04Xd rev:%04Xd",

                          event_list[i].fd, event_list[i].events, revents);

        }

        if (event_list[i].fd == -1) {

            /*

             * the disabled event, a workaround for our possible bug,

             * see the comment below

             */

            continue;

        }

        c = ngx_cycle->files[event_list[i].fd];

        if (c->fd == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");

            /*

             * it is certainly our fault and it should be investigated,

             * in the meantime we disable this event to avoid a CPU spinning

             */

            if (i == nevents - 1) {

                nevents--;

            } else {

                event_list[i].fd = -1;

            }

            continue;

        }

        if ((revents & (POLLERR|POLLHUP|POLLNVAL))

             && (revents & (POLLIN|POLLOUT)) == 0)

        {

            /*

             * if the error events were returned without POLLIN or POLLOUT,

             * then add these flags to handle the events at least in one

             * active handler

             */

            revents |= POLLIN|POLLOUT;

        }

        found = 0;

        if ((revents & POLLIN) && c->read->active) {

            found = 1;

            ev = c->read;

            ev->ready = 1;

            queue = ev->accept ? &ngx_posted_accept_events

                               : &ngx_posted_events;

            ngx_post_event(ev, queue);

        }

        if ((revents & POLLOUT) && c->write->active) {

            found = 1;

            ev = c->write;

            ev->ready = 1;

            ngx_post_event(ev, &ngx_posted_events);

        }

        if (found) {

            ready--;

            continue;

        }

    }

    if (ready != 0) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events");

    }

    return NGX_OK;

}

static char *

‌ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_event_conf_t  *ecf;

    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ecf->use != ngx_poll_module.ctx_index) {

        return NGX_CONF_OK;

    }

#if (NGX_THREADS)

    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,

                  "poll() is not supported in the threaded mode");

    return NGX_CONF_ERROR;

#else

    return NGX_CONF_OK;

#endif

}

src/os/unix/ngx_linux_init.c - nginx-1.7.9

Global variables defined

ngx_linux_io

ngx_linux_kern_osrelease

ngx_linux_kern_ostype

ngx_linux_rtsig_max

Functions defined

ngx_os_specific_init

ngx_os_specific_status

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌u_char  ngx_linux_kern_ostype[50];

‌u_char  ngx_linux_kern_osrelease[50];

‌int     ngx_linux_rtsig_max;

‌static ngx_os_io_t ngx_linux_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

#if (NGX_HAVE_SENDFILE)

    ngx_linux_sendfile_chain,

    NGX_IO_SENDFILE

#else

    ngx_writev_chain,

    0

#endif

};

ngx_int_t

‌ngx_os_specific_init(ngx_log_t *log)

{

    struct utsname  u;

    if (uname(&u) == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed");

        return NGX_ERROR;

    }

    (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname,

                       sizeof(ngx_linux_kern_ostype));

    (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,

                       sizeof(ngx_linux_kern_osrelease));

#if (NGX_HAVE_RTSIG)

    {

    int        name[2];

    size_t     len;

    ngx_err_t  err;

    name[0] = CTL_KERN;

    name[1] = KERN_RTSIGMAX;

    len = sizeof(ngx_linux_rtsig_max);

    if (sysctl(name, 2, &ngx_linux_rtsig_max, &len, NULL, 0) == -1) {

        err = ngx_errno;

        if (err != NGX_ENOTDIR && err != NGX_ENOSYS) {

            ngx_log_error(NGX_LOG_ALERT, log, err,

                          "sysctl(KERN_RTSIGMAX) failed");

            return NGX_ERROR;

        }

        ngx_linux_rtsig_max = 0;

    }

    }

#endif

    ngx_os_io = ngx_linux_io;

    return NGX_OK;

}

void

‌ngx_os_specific_status(ngx_log_t *log)

{

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",

                  ngx_linux_kern_ostype, ngx_linux_kern_osrelease);

#if (NGX_HAVE_RTSIG)

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d",

                  ngx_linux_rtsig_max);

#endif

}

src/os/unix/ngx_linux_sendfile_chain.c - nginx-1.7.9

Functions defined

ngx_linux_sendfile_chain

Macros defined

NGX_SENDFILE_MAXSIZE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

/*

* On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit

* offsets only, and the including <sys/sendfile.h> breaks the compiling,

* if off_t is 64 bit wide.  So we use own sendfile() definition, where offset

* parameter is int32_t, and use sendfile() for the file parts below 2G only,

* see src/os/unix/ngx_linux_config.h

*

* Linux 2.4.21 has the new sendfile64() syscall #239.

*

* On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter

* more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,

* so we limit it to 2G-1 bytes.

*/

‌#define NGX_SENDFILE_MAXSIZE  2147483647L

ngx_chain_t *

‌ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    int            tcp_nodelay;

    off_t          send, prev_send, sent;

    size_t         file_size;

    ssize_t        n;

    ngx_err_t      err;

    ngx_buf_t     *file;

    ngx_uint_t     eintr;

    ngx_event_t   *wev;

    ngx_chain_t   *cl;

    ngx_iovec_t    header;

    struct iovec   headers[NGX_IOVS_PREALLOCATE];

#if (NGX_HAVE_SENDFILE64)

    off_t          offset;

#else

    int32_t        offset;

#endif

    wev = c->write;

    if (!wev->ready) {

        return in;

    }

    /* the maximum limit size is 2G-1 - the page size */

    if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {

        limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;

    }

    send = 0;

    header.iovs = headers;

    header.nalloc = NGX_IOVS_PREALLOCATE;

    for ( ;; ) {

        eintr = 0;

        prev_send = send;

        /* create the iovec and coalesce the neighbouring bufs */

        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);

        if (cl == NGX_CHAIN_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        send += header.size;

        /* set TCP_CORK if there is a header before a file */

        if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET

            && header.count != 0

            && cl

            && cl->buf->in_file)

        {

            /* the TCP_CORK and TCP_NODELAY are mutually exclusive */

            if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {

                tcp_nodelay = 0;

                if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,

                               (const void *) &tcp_nodelay, sizeof(int)) == -1)

                {

                    err = ngx_socket_errno;

                    /*

                     * there is a tiny chance to be interrupted, however,

                     * we continue a processing with the TCP_NODELAY

                     * and without the TCP_CORK

                     */

                    if (err != NGX_EINTR) {

                        wev->error = 1;

                        ngx_connection_error(c, err,

                                             "setsockopt(TCP_NODELAY) failed");

                        return NGX_CHAIN_ERROR;

                    }

                } else {

                    c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;

                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                                   "no tcp_nodelay");

                }

            }

            if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {

                if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {

                    err = ngx_socket_errno;

                    /*

                     * there is a tiny chance to be interrupted, however,

                     * we continue a processing without the TCP_CORK

                     */

                    if (err != NGX_EINTR) {

                        wev->error = 1;

                        ngx_connection_error(c, err,

                                             ngx_tcp_nopush_n " failed");

                        return NGX_CHAIN_ERROR;

                    }

                } else {

                    c->tcp_nopush = NGX_TCP_NOPUSH_SET;

                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,

                                   "tcp_nopush");

                }

            }

        }

        /* get the file buf */

        if (header.count == 0 && cl && cl->buf->in_file && send < limit) {

            file = cl->buf;

            /* coalesce the neighbouring file bufs */

            file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);

            send += file_size;

#if 1

            if (file_size == 0) {

                ngx_debug_point();

                return NGX_CHAIN_ERROR;

            }

#endif

#if (NGX_HAVE_SENDFILE64)

            offset = file->file_pos;

#else

            offset = (int32_t) file->file_pos;

#endif

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "sendfile: @%O %uz", file->file_pos, file_size);

            n = sendfile(c->fd, file->file->fd, &offset, file_size);

            if (n == -1) {

                err = ngx_errno;

                switch (err) {

                case NGX_EAGAIN:

                    break;

                case NGX_EINTR:

                    eintr = 1;

                    break;

                default:

                    wev->error = 1;

                    ngx_connection_error(c, err, "sendfile() failed");

                    return NGX_CHAIN_ERROR;

                }

                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,

                               "sendfile() is not ready");

            }

            sent = n > 0 ? n : 0;

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "sendfile: %z, @%O %O:%uz",

                           n, file->file_pos, sent, file_size);

        } else {

            n = ngx_writev(c, &header);

            if (n == NGX_ERROR) {

                return NGX_CHAIN_ERROR;

            }

            sent = (n == NGX_AGAIN) ? 0 : n;

        }

        c->sent += sent;

        in = ngx_chain_update_sent(in, sent);

        if (eintr) {

            send = prev_send;

            continue;

        }

        if (send - prev_send != sent) {

            wev->ready = 0;

            return in;

        }

        if (send >= limit || in == NULL) {

            return in;

        }

    }

}

src/core/ngx_crc.h - nginx-1.7.9

Functions defined

ngx_crc

Macros defined

_NGX_CRC_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CRC_H_INCLUDED_

‌#define _NGX_CRC_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

/* 32-bit crc16 */

static ngx_inline uint32_t

‌ngx_crc(u_char *data, size_t len)

{

    uint32_t  sum;

    for (sum = 0; len; len--) {

        /*

         * gcc 2.95.2 x86 and icc 7.1.006 compile

         * that operator into the single "rol" opcode,

         * msvc 6.0sp2 compiles it into four opcodes.

         */

        sum = sum >> 1 | sum << 31;

        sum += *data++;

    }

    return sum;

}

#endif /* _NGX_CRC_H_INCLUDED_ */

src/os/unix/ngx_setaffinity.h - nginx-1.7.9

Macros defined

NGX_HAVE_CPU_AFFINITY

_NGX_SETAFFINITY_H_INCLUDED_

ngx_setaffinity

Source code

/*

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SETAFFINITY_H_INCLUDED_

‌#define _NGX_SETAFFINITY_H_INCLUDED_

#if (NGX_HAVE_SCHED_SETAFFINITY || NGX_HAVE_CPUSET_SETAFFINITY)

‌#define NGX_HAVE_CPU_AFFINITY 1

void ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log);

#else

‌#define ngx_setaffinity(cpu_affinity, log)

#endif

#endif /* _NGX_SETAFFINITY_H_INCLUDED_ */

src/core/ngx_crypt.c - nginx-1.7.9

Functions defined

ngx_crypt

ngx_crypt_apr1

ngx_crypt_plain

ngx_crypt_sha

ngx_crypt_ssha

ngx_crypt_to64

Source code

/*

* Copyright (C) Maxim Dounin

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_crypt.h>

#include <ngx_md5.h>

#if (NGX_HAVE_SHA1)

#include <ngx_sha1.h>

#endif

#if (NGX_CRYPT)

static ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

#if (NGX_HAVE_SHA1)

static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

static ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

#endif

static u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n);

ngx_int_t

‌ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    if (ngx_strncmp(salt, "$apr1$", sizeof("$apr1$") - 1) == 0) {

        return ngx_crypt_apr1(pool, key, salt, encrypted);

    } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) {

        return ngx_crypt_plain(pool, key, salt, encrypted);

#if (NGX_HAVE_SHA1)

    } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {

        return ngx_crypt_ssha(pool, key, salt, encrypted);

    } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) {

        return ngx_crypt_sha(pool, key, salt, encrypted);

#endif

    }

    /* fallback to libc crypt() */

    return ngx_libc_crypt(pool, key, salt, encrypted);

}

static ngx_int_t

‌ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    ngx_int_t          n;

    ngx_uint_t         i;

    u_char            *p, *last, final[16];

    size_t             saltlen, keylen;

    ngx_md5_t          md5, ctx1;

    /* Apache's apr1 crypt is Poul-Henning Kamp's md5 crypt with $apr1$ magic */

    keylen = ngx_strlen(key);

    /* true salt: no magic, max 8 chars, stop at first $ */

    salt += sizeof("$apr1$") - 1;

    last = salt + 8;

    for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }

    saltlen = p - salt;

    /* hash key and salt */

    ngx_md5_init(&md5);

    ngx_md5_update(&md5, key, keylen);

    ngx_md5_update(&md5, (u_char *) "$apr1$", sizeof("$apr1$") - 1);

    ngx_md5_update(&md5, salt, saltlen);

    ngx_md5_init(&ctx1);

    ngx_md5_update(&ctx1, key, keylen);

    ngx_md5_update(&ctx1, salt, saltlen);

    ngx_md5_update(&ctx1, key, keylen);

    ngx_md5_final(final, &ctx1);

    for (n = keylen; n > 0; n -= 16) {

        ngx_md5_update(&md5, final, n > 16 ? 16 : n);

    }

    ngx_memzero(final, sizeof(final));

    for (i = keylen; i; i >>= 1) {

        if (i & 1) {

            ngx_md5_update(&md5, final, 1);

        } else {

            ngx_md5_update(&md5, key, 1);

        }

    }

    ngx_md5_final(final, &md5);

    for (i = 0; i < 1000; i++) {

        ngx_md5_init(&ctx1);

        if (i & 1) {

            ngx_md5_update(&ctx1, key, keylen);

        } else {

            ngx_md5_update(&ctx1, final, 16);

        }

        if (i % 3) {

            ngx_md5_update(&ctx1, salt, saltlen);

        }

        if (i % 7) {

            ngx_md5_update(&ctx1, key, keylen);

        }

        if (i & 1) {

            ngx_md5_update(&ctx1, final, 16);

        } else {

            ngx_md5_update(&ctx1, key, keylen);

        }

        ngx_md5_final(final, &ctx1);

    }

    /* output */

    *encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 1 + 22 + 1);

    if (*encrypted == NULL) {

        return NGX_ERROR;

    }

    p = ngx_cpymem(*encrypted, "$apr1$", sizeof("$apr1$") - 1);

    p = ngx_copy(p, salt, saltlen);

    *p++ = '$';

    p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4);

    p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4);

    p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4);

    p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4);

    p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4);

    p = ngx_crypt_to64(p, final[11], 2);

    *p = '\0';

    return NGX_OK;

}

static u_char *

‌ngx_crypt_to64(u_char *p, uint32_t v, size_t n)

{

    static u_char   itoa64[] =

        "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    while (n--) {

       *p++ = itoa64[v & 0x3f];

       v >>= 6;

    }

    return p;

}

static ngx_int_t

‌ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    size_t   len;

    u_char  *p;

    len = ngx_strlen(key);

    *encrypted = ngx_pnalloc(pool, sizeof("{PLAIN}") - 1 + len + 1);

    if (*encrypted == NULL) {

        return NGX_ERROR;

    }

    p = ngx_cpymem(*encrypted, "{PLAIN}", sizeof("{PLAIN}") - 1);

    ngx_memcpy(p, key, len + 1);

    return NGX_OK;

}

#if (NGX_HAVE_SHA1)

static ngx_int_t

‌ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    size_t       len;

    ngx_int_t    rc;

    ngx_str_t    encoded, decoded;

    ngx_sha1_t   sha1;

    /* "{SSHA}" base64(SHA1(key salt) salt) */

    /* decode base64 salt to find out true salt */

    encoded.data = salt + sizeof("{SSHA}") - 1;

    encoded.len = ngx_strlen(encoded.data);

    len = ngx_max(ngx_base64_decoded_length(encoded.len), 20);

    decoded.data = ngx_pnalloc(pool, len);

    if (decoded.data == NULL) {

        return NGX_ERROR;

    }

    rc = ngx_decode_base64(&decoded, &encoded);

    if (rc != NGX_OK || decoded.len < 20) {

        decoded.len = 20;

    }

    /* update SHA1 from key and salt */

    ngx_sha1_init(&sha1);

    ngx_sha1_update(&sha1, key, ngx_strlen(key));

    ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20);

    ngx_sha1_final(decoded.data, &sha1);

    /* encode it back to base64 */

    len = sizeof("{SSHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;

    *encrypted = ngx_pnalloc(pool, len);

    if (*encrypted == NULL) {

        return NGX_ERROR;

    }

    encoded.data = ngx_cpymem(*encrypted, "{SSHA}", sizeof("{SSHA}") - 1);

    ngx_encode_base64(&encoded, &decoded);

    encoded.data[encoded.len] = '\0';

    return NGX_OK;

}

static ngx_int_t

‌ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)

{

    size_t      len;

    ngx_str_t   encoded, decoded;

    ngx_sha1_t  sha1;

    u_char      digest[20];

    /* "{SHA}" base64(SHA1(key)) */

    decoded.len = sizeof(digest);

    decoded.data = digest;

    ngx_sha1_init(&sha1);

    ngx_sha1_update(&sha1, key, ngx_strlen(key));

    ngx_sha1_final(digest, &sha1);

    len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;

    *encrypted = ngx_pnalloc(pool, len);

    if (*encrypted == NULL) {

        return NGX_ERROR;

    }

    encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1);

    ngx_encode_base64(&encoded, &decoded);

    encoded.data[encoded.len] = '\0';

    return NGX_OK;

}

#endif /* NGX_HAVE_SHA1 */

#endif /* NGX_CRYPT */

src/core/ngx_sha1.h - nginx-1.7.9

Data types defined

ngx_sha1_t

Macros defined

_NGX_SHA1_H_INCLUDED_

ngx_sha1_final

ngx_sha1_init

ngx_sha1_update

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SHA1_H_INCLUDED_

‌#define _NGX_SHA1_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_HAVE_OPENSSL_SHA1_H)

#include <openssl/sha.h>

#else

#include <sha.h>

#endif

‌typedef SHA_CTX  ngx_sha1_t;

‌#define ngx_sha1_init    SHA1_Init

‌#define ngx_sha1_update  SHA1_Update

‌#define ngx_sha1_final   SHA1_Final

#endif /* _NGX_SHA1_H_INCLUDED_ */

src/core/ngx_crypt.h - nginx-1.7.9

Macros defined

_NGX_CRYPT_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_CRYPT_H_INCLUDED_

‌#define _NGX_CRYPT_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,

    u_char **encrypted);

#endif /* _NGX_CRYPT_H_INCLUDED_ */

src/core/ngx_murmurhash.c - nginx-1.7.9

Functions defined

ngx_murmur_hash2

Source code

/*

* Copyright (C) Austin Appleby

*/

#include <ngx_config.h>

#include <ngx_core.h>

uint32_t

‌ngx_murmur_hash2(u_char *data, size_t len)

{

    uint32_t  h, k;

    h = 0 ^ len;

    while (len >= 4) {

        k  = data[0];

        k |= data[1] << 8;

        k |= data[2] << 16;

        k |= data[3] << 24;

        k *= 0x5bd1e995;

        k ^= k >> 24;

        k *= 0x5bd1e995;

        h *= 0x5bd1e995;

        h ^= k;

        data += 4;

        len -= 4;

    }

    switch (len) {

    case 3:

        h ^= data[2] << 16;

    case 2:

        h ^= data[1] << 8;

    case 1:

        h ^= data[0];

        h *= 0x5bd1e995;

    }

    h ^= h >> 13;

    h *= 0x5bd1e995;

    h ^= h >> 15;

    return h;

}

src/core/ngx_murmurhash.h - nginx-1.7.9

Macros defined

_NGX_MURMURHASH_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_MURMURHASH_H_INCLUDED_

‌#define _NGX_MURMURHASH_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

uint32_t ngx_murmur_hash2(u_char *data, size_t len);

#endif /* _NGX_MURMURHASH_H_INCLUDED_ */

src/core/ngx_parse.h - nginx-1.7.9

Macros defined

_NGX_PARSE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_PARSE_H_INCLUDED_

‌#define _NGX_PARSE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

ssize_t ngx_parse_size(ngx_str_t *line);

off_t ngx_parse_offset(ngx_str_t *line);

ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec);

#endif /* _NGX_PARSE_H_INCLUDED_ */

src/core/ngx_radix_tree.c - nginx-1.7.9

Functions defined

ngx_radix128tree_delete

ngx_radix128tree_find

ngx_radix128tree_insert

ngx_radix32tree_delete

ngx_radix32tree_find

ngx_radix32tree_insert

ngx_radix_alloc

ngx_radix_tree_create

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

static ngx_radix_node_t *ngx_radix_alloc(ngx_radix_tree_t *tree);

ngx_radix_tree_t *

‌ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)

{

    uint32_t           key, mask, inc;

    ngx_radix_tree_t  *tree;

    tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));

    if (tree == NULL) {

        return NULL;

    }

    tree->pool = pool;

    tree->free = NULL;

    tree->start = NULL;

    tree->size = 0;

    tree->root = ngx_radix_alloc(tree);

    if (tree->root == NULL) {

        return NULL;

    }

    tree->root->right = NULL;

    tree->root->left = NULL;

    tree->root->parent = NULL;

    tree->root->value = NGX_RADIX_NO_VALUE;

    if (preallocate == 0) {

        return tree;

    }

    /*

     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.

     * increases TLB hits even if for first lookup iterations.

     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,

     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits

     * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to

     * to preallocate more than one page, because further preallocation

     * distributes the only bit per page.  Instead, a random insertion

     * may distribute several bits per page.

     *

     * Thus, by default we preallocate maximum

     *     6 bits on amd64 (64-bit platform and 4K pages)

     *     7 bits on i386 (32-bit platform and 4K pages)

     *     7 bits on sparc64 in 64-bit mode (8K pages)

     *     8 bits on sparc64 in 32-bit mode (8K pages)

     */

    if (preallocate == -1) {

        switch (ngx_pagesize / sizeof(ngx_radix_node_t)) {

        /* amd64 */

        case 128:

            preallocate = 6;

            break;

        /* i386, sparc64 */

        case 256:

            preallocate = 7;

            break;

        /* sparc64 in 32-bit mode */

        default:

            preallocate = 8;

        }

    }

    mask = 0;

    inc = 0x80000000;

    while (preallocate--) {

        key = 0;

        mask >>= 1;

        mask |= 0x80000000;

        do {

            if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)

                != NGX_OK)

            {

                return NULL;

            }

            key += inc;

        } while (key);

        inc >>= 1;

    }

    return tree;

}

ngx_int_t

‌ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask,

    uintptr_t value)

{

    uint32_t           bit;

    ngx_radix_node_t  *node, *next;

    bit = 0x80000000;

    node = tree->root;

    next = tree->root;

    while (bit & mask) {

        if (key & bit) {

            next = node->right;

        } else {

            next = node->left;

        }

        if (next == NULL) {

            break;

        }

        bit >>= 1;

        node = next;

    }

    if (next) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            return NGX_BUSY;

        }

        node->value = value;

        return NGX_OK;

    }

    while (bit & mask) {

        next = ngx_radix_alloc(tree);

        if (next == NULL) {

            return NGX_ERROR;

        }

        next->right = NULL;

        next->left = NULL;

        next->parent = node;

        next->value = NGX_RADIX_NO_VALUE;

        if (key & bit) {

            node->right = next;

        } else {

            node->left = next;

        }

        bit >>= 1;

        node = next;

    }

    node->value = value;

    return NGX_OK;

}

ngx_int_t

‌ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)

{

    uint32_t           bit;

    ngx_radix_node_t  *node;

    bit = 0x80000000;

    node = tree->root;

    while (node && (bit & mask)) {

        if (key & bit) {

            node = node->right;

        } else {

            node = node->left;

        }

        bit >>= 1;

    }

    if (node == NULL) {

        return NGX_ERROR;

    }

    if (node->right || node->left) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            node->value = NGX_RADIX_NO_VALUE;

            return NGX_OK;

        }

        return NGX_ERROR;

    }

    for ( ;; ) {

        if (node->parent->right == node) {

            node->parent->right = NULL;

        } else {

            node->parent->left = NULL;

        }

        node->right = tree->free;

        tree->free = node;

        node = node->parent;

        if (node->right || node->left) {

            break;

        }

        if (node->value != NGX_RADIX_NO_VALUE) {

            break;

        }

        if (node->parent == NULL) {

            break;

        }

    }

    return NGX_OK;

}

uintptr_t

‌ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)

{

    uint32_t           bit;

    uintptr_t          value;

    ngx_radix_node_t  *node;

    bit = 0x80000000;

    value = NGX_RADIX_NO_VALUE;

    node = tree->root;

    while (node) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            value = node->value;

        }

        if (key & bit) {

            node = node->right;

        } else {

            node = node->left;

        }

        bit >>= 1;

    }

    return value;

}

#if (NGX_HAVE_INET6)

ngx_int_t

‌ngx_radix128tree_insert(ngx_radix_tree_t *tree, u_char *key, u_char *mask,

    uintptr_t value)

{

    u_char             bit;

    ngx_uint_t         i;

    ngx_radix_node_t  *node, *next;

    i = 0;

    bit = 0x80;

    node = tree->root;

    next = tree->root;

    while (bit & mask[i]) {

        if (key[i] & bit) {

            next = node->right;

        } else {

            next = node->left;

        }

        if (next == NULL) {

            break;

        }

        bit >>= 1;

        node = next;

        if (bit == 0) {

            if (++i == 16) {

                break;

            }

            bit = 0x80;

        }

    }

    if (next) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            return NGX_BUSY;

        }

        node->value = value;

        return NGX_OK;

    }

    while (bit & mask[i]) {

        next = ngx_radix_alloc(tree);

        if (next == NULL) {

            return NGX_ERROR;

        }

        next->right = NULL;

        next->left = NULL;

        next->parent = node;

        next->value = NGX_RADIX_NO_VALUE;

        if (key[i] & bit) {

            node->right = next;

        } else {

            node->left = next;

        }

        bit >>= 1;

        node = next;

        if (bit == 0) {

            if (++i == 16) {

                break;

            }

            bit = 0x80;

        }

    }

    node->value = value;

    return NGX_OK;

}

ngx_int_t

‌ngx_radix128tree_delete(ngx_radix_tree_t *tree, u_char *key, u_char *mask)

{

    u_char             bit;

    ngx_uint_t         i;

    ngx_radix_node_t  *node;

    i = 0;

    bit = 0x80;

    node = tree->root;

    while (node && (bit & mask[i])) {

        if (key[i] & bit) {

            node = node->right;

        } else {

            node = node->left;

        }

        bit >>= 1;

        if (bit == 0) {

            if (++i == 16) {

                break;

            }

            bit = 0x80;

        }

    }

    if (node == NULL) {

        return NGX_ERROR;

    }

    if (node->right || node->left) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            node->value = NGX_RADIX_NO_VALUE;

            return NGX_OK;

        }

        return NGX_ERROR;

    }

    for ( ;; ) {

        if (node->parent->right == node) {

            node->parent->right = NULL;

        } else {

            node->parent->left = NULL;

        }

        node->right = tree->free;

        tree->free = node;

        node = node->parent;

        if (node->right || node->left) {

            break;

        }

        if (node->value != NGX_RADIX_NO_VALUE) {

            break;

        }

        if (node->parent == NULL) {

            break;

        }

    }

    return NGX_OK;

}

uintptr_t

‌ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key)

{

    u_char             bit;

    uintptr_t          value;

    ngx_uint_t         i;

    ngx_radix_node_t  *node;

    i = 0;

    bit = 0x80;

    value = NGX_RADIX_NO_VALUE;

    node = tree->root;

    while (node) {

        if (node->value != NGX_RADIX_NO_VALUE) {

            value = node->value;

        }

        if (key[i] & bit) {

            node = node->right;

        } else {

            node = node->left;

        }

        bit >>= 1;

        if (bit == 0) {

            i++;

            bit = 0x80;

        }

    }

    return value;

}

#endif

static ngx_radix_node_t *

‌ngx_radix_alloc(ngx_radix_tree_t *tree)

{

    ngx_radix_node_t  *p;

    if (tree->free) {

        p = tree->free;

        tree->free = tree->free->right;

        return p;

    }

    if (tree->size < sizeof(ngx_radix_node_t)) {

        tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);

        if (tree->start == NULL) {

            return NULL;

        }

        tree->size = ngx_pagesize;

    }

    p = (ngx_radix_node_t *) tree->start;

    tree->start += sizeof(ngx_radix_node_t);

    tree->size -= sizeof(ngx_radix_node_t);

    return p;

}

src/core/ngx_radix_tree.h - nginx-1.7.9

Data types defined

ngx_radix_node_s

ngx_radix_node_t

ngx_radix_tree_t

Macros defined

NGX_RADIX_NO_VALUE

_NGX_RADIX_TREE_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_RADIX_TREE_H_INCLUDED_

‌#define _NGX_RADIX_TREE_H_INCLUDED_

#include <ngx_config.h>

#include <ngx_core.h>

‌#define NGX_RADIX_NO_VALUE   (uintptr_t) -1

‌typedef struct ngx_radix_node_s  ngx_radix_node_t;

‌struct ngx_radix_node_s {

    ngx_radix_node_t  *right;

    ngx_radix_node_t  *left;

    ngx_radix_node_t  *parent;

    uintptr_t          value;

};

typedef struct {

    ngx_radix_node_t  *root;

    ngx_pool_t        *pool;

    ngx_radix_node_t  *free;

    char              *start;

    size_t             size;

‌} ngx_radix_tree_t;

ngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool,

    ngx_int_t preallocate);

ngx_int_t ngx_radix32tree_insert(ngx_radix_tree_t *tree,

    uint32_t key, uint32_t mask, uintptr_t value);

ngx_int_t ngx_radix32tree_delete(ngx_radix_tree_t *tree,

    uint32_t key, uint32_t mask);

uintptr_t ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key);

#if (NGX_HAVE_INET6)

ngx_int_t ngx_radix128tree_insert(ngx_radix_tree_t *tree,

    u_char *key, u_char *mask, uintptr_t value);

ngx_int_t ngx_radix128tree_delete(ngx_radix_tree_t *tree,

    u_char *key, u_char *mask);

uintptr_t ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key);

#endif

#endif /* _NGX_RADIX_TREE_H_INCLUDED_ */

src/event/ - nginx-1.7.9

modules/

ngx_event.c

ngx_event.h

ngx_event_accept.c

ngx_event_busy_lock.c

ngx_event_busy_lock.h

ngx_event_connect.c

ngx_event_connect.h

ngx_event_mutex.c

ngx_event_openssl.c

ngx_event_openssl.h

ngx_event_openssl_stapling.c

ngx_event_pipe.c

ngx_event_pipe.h

ngx_event_posted.c

ngx_event_posted.h

ngx_event_timer.c

ngx_event_timer.h

src/event/modules/ - nginx-1.7.9

ngx_aio_module.c

ngx_devpoll_module.c

ngx_epoll_module.c

ngx_eventport_module.c

ngx_kqueue_module.c

ngx_poll_module.c

ngx_rtsig_module.c

ngx_select_module.c

ngx_win32_select_module.c

src/event/modules/ngx_aio_module.c - nginx-1.7.9

Global variables defined

aio_name

ngx_aio_module

ngx_aio_module_ctx

ngx_os_aio

Functions defined

ngx_aio_add_event

ngx_aio_del_connection

ngx_aio_del_event

ngx_aio_done

ngx_aio_init

ngx_aio_process_events

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

extern ngx_event_module_t  ngx_kqueue_module_ctx;

static ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_aio_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags);

static ngx_int_t ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

‌ngx_os_io_t ngx_os_aio = {

    ngx_aio_read,

    ngx_aio_read_chain,

    NULL,

    ngx_aio_write,

    ngx_aio_write_chain,

    0

};

‌static ngx_str_t      aio_name = ngx_string("aio");

‌ngx_event_module_t  ngx_aio_module_ctx = {

    &aio_name,

    NULL,                                  /* create configuration */

    NULL,                                  /* init configuration */

    {

        ngx_aio_add_event,                 /* add an event */

        ngx_aio_del_event,                 /* delete an event */

        NULL,                              /* enable an event */

        NULL,                              /* disable an event */

        NULL,                              /* add an connection */

        ngx_aio_del_connection,            /* delete an connection */

        NULL,                              /* process the changes */

        ngx_aio_process_events,            /* process the events */

        ngx_aio_init,                      /* init the events */

        ngx_aio_done                       /* done the events */

    }

};

‌ngx_module_t  ngx_aio_module = {

    NGX_MODULE_V1,

    &ngx_aio_module_ctx,                   /* module context */

    NULL,                                  /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

#if (NGX_HAVE_KQUEUE)

static ngx_int_t

‌ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    if (ngx_kqueue_module_ctx.actions.init(cycle, timer) == NGX_ERROR) {

        return NGX_ERROR;

    }

    ngx_io = ngx_os_aio;

    ngx_event_flags = NGX_USE_AIO_EVENT;

    ngx_event_actions = ngx_aio_module_ctx.actions;

    return NGX_OK;

}

static void

‌ngx_aio_done(ngx_cycle_t *cycle)

{

    ngx_kqueue_module_ctx.actions.done(cycle);

}

/* the event adding and deleting are needed for the listening sockets */

static ngx_int_t

‌ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    return ngx_kqueue_module_ctx.actions.add(ev, event, flags);

}

static ngx_int_t

‌ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    return ngx_kqueue_module_ctx.actions.del(ev, event, flags);

}

static ngx_int_t

‌ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags)

{

    int  rc;

    if (c->read->active == 0 && c->write->active == 0) {

        return NGX_OK;

    }

    if (flags & NGX_CLOSE_EVENT) {

        return NGX_OK;

    }

    rc = aio_cancel(c->fd, NULL);

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc);

    if (rc == AIO_CANCELED) {

        c->read->active = 0;

        c->write->active = 0;

        return NGX_OK;

    }

    if (rc == AIO_ALLDONE) {

        c->read->active = 0;

        c->write->active = 0;

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "aio_cancel() returned AIO_ALLDONE");

        return NGX_OK;

    }

    if (rc == -1) {

        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,

                      "aio_cancel() failed");

        return NGX_ERROR;

    }

    if (rc == AIO_NOTCANCELED) {

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                      "aio_cancel() returned AIO_NOTCANCELED");

        return NGX_ERROR;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)

{

    return ngx_kqueue_module_ctx.actions.process_events(cycle, timer, flags);

}

#endif /* NGX_HAVE_KQUEUE */

src/event/modules/ngx_win32_select_module.c - nginx-1.7.9

Global variables defined

event_index

master_read_fd_set

master_write_fd_set

max_read

max_write

nevents

ngx_select_module

ngx_select_module_ctx

select_name

work_read_fd_set

work_write_fd_set

Functions defined

ngx_select_add_event

ngx_select_del_event

ngx_select_done

ngx_select_init

ngx_select_init_conf

ngx_select_process_events

ngx_select_repair_fd_sets

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);

static void ngx_select_done(ngx_cycle_t *cycle);

static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,

    ngx_uint_t flags);

static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags);

static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);

static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);

‌static fd_set         master_read_fd_set;

‌static fd_set         master_write_fd_set;

‌static fd_set         work_read_fd_set;

‌static fd_set         work_write_fd_set;

‌static ngx_uint_t     max_read;

‌static ngx_uint_t     max_write;

‌static ngx_uint_t     nevents;

‌static ngx_event_t  **event_index;

‌static ngx_str_t    select_name = ngx_string("select");

‌ngx_event_module_t  ngx_select_module_ctx = {

    &select_name,

    NULL,                                  /* create configuration */

    ngx_select_init_conf,                  /* init configuration */

    {

        ngx_select_add_event,              /* add an event */

        ngx_select_del_event,              /* delete an event */

        ngx_select_add_event,              /* enable an event */

        ngx_select_del_event,              /* disable an event */

        NULL,                              /* add an connection */

        NULL,                              /* delete an connection */

        NULL,                              /* process the changes */

        ngx_select_process_events,         /* process the events */

        ngx_select_init,                   /* init the events */

        ngx_select_done                    /* done the events */

    }

};

‌ngx_module_t  ngx_select_module = {

    NGX_MODULE_V1,

    &ngx_select_module_ctx,                /* module context */

    NULL,                                  /* module directives */

    NGX_EVENT_MODULE,                      /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)

{

    ngx_event_t  **index;

    if (event_index == NULL) {

        FD_ZERO(&master_read_fd_set);

        FD_ZERO(&master_write_fd_set);

        nevents = 0;

    }

    if (ngx_process >= NGX_PROCESS_WORKER

        || cycle->old_cycle == NULL

        || cycle->old_cycle->connection_n < cycle->connection_n)

    {

        index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,

                          cycle->log);

        if (index == NULL) {

            return NGX_ERROR;

        }

        if (event_index) {

            ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);

            ngx_free(event_index);

        }

        event_index = index;

    }

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_select_module_ctx.actions;

    ngx_event_flags = NGX_USE_LEVEL_EVENT;

    max_read = 0;

    max_write = 0;

    return NGX_OK;

}

static void

‌ngx_select_done(ngx_cycle_t *cycle)

{

    ngx_free(event_index);

    event_index = NULL;

}

static ngx_int_t

‌ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_connection_t  *c;

    c = ev->data;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "select add event fd:%d ev:%i", c->fd, event);

    if (ev->index != NGX_INVALID_INDEX) {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "select event fd:%d ev:%i is already set", c->fd, event);

        return NGX_OK;

    }

    if ((event == NGX_READ_EVENT && ev->write)

        || (event == NGX_WRITE_EVENT && !ev->write))

    {

        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,

                      "invalid select %s event fd:%d ev:%i",

                      ev->write ? "write" : "read", c->fd, event);

        return NGX_ERROR;

    }

    if ((event == NGX_READ_EVENT && max_read >= FD_SETSIZE)

        || (event == NGX_WRITE_EVENT && max_write >= FD_SETSIZE))

    {

        ngx_log_error(NGX_LOG_ERR, ev->log, 0,

                      "maximum number of descriptors "

                      "supported by select() is %d", FD_SETSIZE);

        return NGX_ERROR;

    }

    if (event == NGX_READ_EVENT) {

        FD_SET(c->fd, &master_read_fd_set);

        max_read++;

    } else if (event == NGX_WRITE_EVENT) {

        FD_SET(c->fd, &master_write_fd_set);

        max_write++;

    }

    ev->active = 1;

    event_index[nevents] = ev;

    ev->index = nevents;

    nevents++;

    return NGX_OK;

}

static ngx_int_t

‌ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

{

    ngx_event_t       *e;

    ngx_connection_t  *c;

    c = ev->data;

    ev->active = 0;

    if (ev->index == NGX_INVALID_INDEX) {

        return NGX_OK;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "select del event fd:%d ev:%i", c->fd, event);

    if (event == NGX_READ_EVENT) {

        FD_CLR(c->fd, &master_read_fd_set);

        max_read--;

    } else if (event == NGX_WRITE_EVENT) {

        FD_CLR(c->fd, &master_write_fd_set);

        max_write--;

    }

    if (ev->index < --nevents) {

        e = event_index[nevents];

        event_index[ev->index] = e;

        e->index = ev->index;

    }

    ev->index = NGX_INVALID_INDEX;

    return NGX_OK;

}

static ngx_int_t

‌ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,

    ngx_uint_t flags)

{

    int                ready, nready;

    ngx_err_t          err;

    ngx_uint_t         i, found;

    ngx_event_t       *ev;

    ngx_queue_t       *queue;

    struct timeval     tv, *tp;

    ngx_connection_t  *c;

#if (NGX_DEBUG)

    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {

        for (i = 0; i < nevents; i++) {

            ev = event_index[i];

            c = ev->data;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                           "select event: fd:%d wr:%d", c->fd, ev->write);

        }

    }

#endif

    if (timer == NGX_TIMER_INFINITE) {

        tp = NULL;

    } else {

        tv.tv_sec = (long) (timer / 1000);

        tv.tv_usec = (long) ((timer % 1000) * 1000);

        tp = &tv;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "select timer: %M", timer);

    work_read_fd_set = master_read_fd_set;

    work_write_fd_set = master_write_fd_set;

    if (max_read || max_write) {

        ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);

    } else {

        /*

         * Winsock select() requires that at least one descriptor set must be

         * be non-null, and any non-null descriptor set must contain at least

         * one handle to a socket.  Otherwise select() returns WSAEINVAL.

         */

        ngx_msleep(timer);

        ready = 0;

    }

    err = (ready == -1) ? ngx_socket_errno : 0;

    if (flags & NGX_UPDATE_TIME) {

        ngx_time_update();

    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "select ready %d", ready);

    if (err) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");

        if (err == WSAENOTSOCK) {

            ngx_select_repair_fd_sets(cycle);

        }

        return NGX_ERROR;

    }

    if (ready == 0) {

        if (timer != NGX_TIMER_INFINITE) {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "select() returned no events without timeout");

        return NGX_ERROR;

    }

    nready = 0;

    for (i = 0; i < nevents; i++) {

        ev = event_index[i];

        c = ev->data;

        found = 0;

        if (ev->write) {

            if (FD_ISSET(c->fd, &work_write_fd_set)) {

                found = 1;

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "select write %d", c->fd);

            }

        } else {

            if (FD_ISSET(c->fd, &work_read_fd_set)) {

                found = 1;

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                               "select read %d", c->fd);

            }

        }

        if (found) {

            ev->ready = 1;

            queue = ev->accept ? &ngx_posted_accept_events

                               : &ngx_posted_events;

            ngx_post_event(ev, queue);

            nready++;

        }

    }

    if (ready != nready) {

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

                      "select ready != events: %d:%d", ready, nready);

        ngx_select_repair_fd_sets(cycle);

    }

    return NGX_OK;

}

static void

‌ngx_select_repair_fd_sets(ngx_cycle_t *cycle)

{

    int           n;

    u_int         i;

    socklen_t     len;

    ngx_err_t     err;

    ngx_socket_t  s;

    for (i = 0; i < master_read_fd_set.fd_count; i++) {

        s = master_read_fd_set.fd_array[i];

        len = sizeof(int);

        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {

            err = ngx_socket_errno;

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "invalid descriptor #%d in read fd_set", s);

            FD_CLR(s, &master_read_fd_set);

        }

    }

    for (i = 0; i < master_write_fd_set.fd_count; i++) {

        s = master_write_fd_set.fd_array[i];

        len = sizeof(int);

        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {

            err = ngx_socket_errno;

            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,

                          "invalid descriptor #%d in write fd_set", s);

            FD_CLR(s, &master_write_fd_set);

        }

    }

}

static char *

‌ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)

{

    ngx_event_conf_t  *ecf;

    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ecf->use != ngx_select_module.ctx_index) {

        return NGX_CONF_OK;

    }

    return NGX_CONF_OK;

}

src/event/ngx_event_mutex.c - nginx-1.7.9

Functions defined

ngx_event_mutex_timedlock

ngx_event_mutex_unlock

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

‌ngx_int_t ngx_event_mutex_timedlock(ngx_event_mutex_t *m, ngx_msec_t timer,

                                    ngx_event_t *ev)

{

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "lock event mutex %p lock:%XD", m, m->lock);

    if (m->lock) {

        if (m->events == NULL) {

            m->events = ev;

        } else {

            m->last->next = ev;

        }

        m->last = ev;

        ev->next = NULL;

#if (NGX_THREADS0)

        ev->light = 1;

#endif

        ngx_add_timer(ev, timer);

        return NGX_AGAIN;

    }

    m->lock = 1;

    return NGX_OK;

}

‌ngx_int_t ngx_event_mutex_unlock(ngx_event_mutex_t *m, ngx_log_t *log)

{

    ngx_event_t  *ev;

    if (m->lock == 0) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "tring to unlock the free event mutex %p", m);

        return NGX_ERROR;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,

                   "unlock event mutex %p, next event: %p", m, m->events);

    m->lock = 0;

    if (m->events) {

        ev = m->events;

        m->events = ev->next;

        ev->next = ngx_posted_events;

        ngx_posted_events = ev;

    }

    return NGX_OK;

}

src/http/ - nginx-1.7.9

modules/

ngx_http.c

ngx_http.h

ngx_http_busy_lock.c

ngx_http_busy_lock.h

ngx_http_cache.h

ngx_http_config.h

ngx_http_copy_filter_module.c

ngx_http_core_module.c

ngx_http_core_module.h

ngx_http_file_cache.c

ngx_http_header_filter_module.c

ngx_http_parse.c

ngx_http_parse_time.c

ngx_http_postpone_filter_module.c

ngx_http_request.c

ngx_http_request.h

ngx_http_request_body.c

ngx_http_script.c

ngx_http_script.h

ngx_http_spdy.c

ngx_http_spdy.h

ngx_http_spdy_filter_module.c

ngx_http_spdy_module.c

ngx_http_spdy_module.h

ngx_http_special_response.c

ngx_http_upstream.c

ngx_http_upstream.h

ngx_http_upstream_round_robin.c

ngx_http_upstream_round_robin.h

ngx_http_variables.c

ngx_http_variables.h

ngx_http_write_filter_module.c

src/http/modules/ - nginx-1.7.9

ngx_http_access_module.c

ngx_http_addition_filter_module.c

ngx_http_auth_basic_module.c

ngx_http_auth_request_module.c

ngx_http_autoindex_module.c

ngx_http_browser_module.c

ngx_http_charset_filter_module.c

ngx_http_chunked_filter_module.c

ngx_http_dav_module.c

ngx_http_degradation_module.c

ngx_http_empty_gif_module.c

ngx_http_fastcgi_module.c

ngx_http_flv_module.c

ngx_http_geo_module.c

ngx_http_geoip_module.c

ngx_http_gunzip_filter_module.c

ngx_http_gzip_filter_module.c

ngx_http_gzip_static_module.c

ngx_http_headers_filter_module.c

ngx_http_image_filter_module.c

ngx_http_index_module.c

ngx_http_limit_conn_module.c

ngx_http_limit_req_module.c

ngx_http_log_module.c

ngx_http_map_module.c

ngx_http_memcached_module.c

ngx_http_mp4_module.c

ngx_http_not_modified_filter_module.c

ngx_http_proxy_module.c

ngx_http_random_index_module.c

ngx_http_range_filter_module.c

ngx_http_realip_module.c

ngx_http_referer_module.c

ngx_http_rewrite_module.c

ngx_http_scgi_module.c

ngx_http_secure_link_module.c

ngx_http_split_clients_module.c

ngx_http_ssi_filter_module.c

ngx_http_ssi_filter_module.h

ngx_http_ssl_module.c

ngx_http_ssl_module.h

ngx_http_static_module.c

ngx_http_stub_status_module.c

ngx_http_sub_filter_module.c

ngx_http_upstream_hash_module.c

ngx_http_upstream_ip_hash_module.c

ngx_http_upstream_keepalive_module.c

ngx_http_upstream_least_conn_module.c

ngx_http_userid_filter_module.c

ngx_http_uwsgi_module.c

ngx_http_xslt_filter_module.c

perl/

src/http/modules/ngx_http_access_module.c - nginx-1.7.9

Global variables defined

ngx_http_access_commands

ngx_http_access_module

ngx_http_access_module_ctx

Data types defined

ngx_http_access_loc_conf_t

ngx_http_access_rule6_t

ngx_http_access_rule_t

ngx_http_access_rule_un_t

Functions defined

ngx_http_access_create_loc_conf

ngx_http_access_found

ngx_http_access_handler

ngx_http_access_inet

ngx_http_access_inet6

ngx_http_access_init

ngx_http_access_merge_loc_conf

ngx_http_access_rule

ngx_http_access_unix

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    in_addr_t         mask;

    in_addr_t         addr;

    ngx_uint_t        deny;      /* unsigned  deny:1; */

‌} ngx_http_access_rule_t;

#if (NGX_HAVE_INET6)

typedef struct {

    struct in6_addr   addr;

    struct in6_addr   mask;

    ngx_uint_t        deny;      /* unsigned  deny:1; */

‌} ngx_http_access_rule6_t;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

typedef struct {

    ngx_uint_t        deny;      /* unsigned  deny:1; */

‌} ngx_http_access_rule_un_t;

#endif

typedef struct {

    ngx_array_t      *rules;     /* array of ngx_http_access_rule_t */

#if (NGX_HAVE_INET6)

    ngx_array_t      *rules6;    /* array of ngx_http_access_rule6_t */

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    ngx_array_t      *rules_un;  /* array of ngx_http_access_rule_un_t */

#endif

‌} ngx_http_access_loc_conf_t;

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

#if (NGX_HAVE_UNIX_DOMAIN)

static ngx_int_t ngx_http_access_unix(ngx_http_request_t *r,

    ngx_http_access_loc_conf_t *alcf);

#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);

‌static ngx_command_t  ngx_http_access_commands[] = {

    { ngx_string("allow"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF

                        |NGX_CONF_TAKE1,

      ngx_http_access_rule,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("deny"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF

                        |NGX_CONF_TAKE1,

      ngx_http_access_rule,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_access_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_access_init,                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_access_create_loc_conf,       /* create location configuration */

    ngx_http_access_merge_loc_conf         /* merge location configuration */

};

‌ngx_module_t  ngx_http_access_module = {

    NGX_MODULE_V1,

    &ngx_http_access_module_ctx,           /* module context */

    ngx_http_access_commands,              /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_access_handler(ngx_http_request_t *r)

{

    struct sockaddr_in          *sin;

    ngx_http_access_loc_conf_t  *alcf;

#if (NGX_HAVE_INET6)

    u_char                      *p;

    in_addr_t                    addr;

    struct sockaddr_in6         *sin6;

#endif

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);

    switch (r->connection->sockaddr->sa_family) {

    case AF_INET:

        if (alcf->rules) {

            sin = (struct sockaddr_in *) r->connection->sockaddr;

            return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);

        }

        break;

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;

        p = sin6->sin6_addr.s6_addr;

        if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {

            addr = p[12] << 24;

            addr += p[13] << 16;

            addr += p[14] << 8;

            addr += p[15];

            return ngx_http_access_inet(r, alcf, htonl(addr));

        }

        if (alcf->rules6) {

            return ngx_http_access_inet6(r, alcf, p);

        }

        break;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    case AF_UNIX:

        if (alcf->rules_un) {

            return ngx_http_access_unix(r, alcf);

        }

        break;

#endif

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,

    in_addr_t addr)

{

    ngx_uint_t               i;

    ngx_http_access_rule_t  *rule;

    rule = alcf->rules->elts;

    for (i = 0; i < alcf->rules->nelts; i++) {

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "access: %08XD %08XD %08XD",

                       addr, rule[i].mask, rule[i].addr);

        if ((addr & rule[i].mask) == rule[i].addr) {

            return ngx_http_access_found(r, rule[i].deny);

        }

    }

    return NGX_DECLINED;

}

#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)

{

    ngx_uint_t                n;

    ngx_uint_t                i;

    ngx_http_access_rule6_t  *rule6;

    rule6 = alcf->rules6->elts;

    for (i = 0; i < alcf->rules6->nelts; i++) {

#if (NGX_DEBUG)

        {

        size_t  cl, ml, al;

        u_char  ct[NGX_INET6_ADDRSTRLEN];

        u_char  mt[NGX_INET6_ADDRSTRLEN];

        u_char  at[NGX_INET6_ADDRSTRLEN];

        cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);

        ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);

        al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);

        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "access: %*s %*s %*s", cl, ct, ml, mt, al, at);

        }

#endif

        for (n = 0; n < 16; n++) {

            if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {

                goto next;

            }

        }

        return ngx_http_access_found(r, rule6[i].deny);

    next:

        continue;

    }

    return NGX_DECLINED;

}

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

static ngx_int_t

‌ngx_http_access_unix(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf)

{

    ngx_uint_t                  i;

    ngx_http_access_rule_un_t  *rule_un;

    rule_un = alcf->rules_un->elts;

    for (i = 0; i < alcf->rules_un->nelts; i++) {

        /* TODO: check path */

        if (1) {

            return ngx_http_access_found(r, rule_un[i].deny);

        }

    }

    return NGX_DECLINED;

}

#endif

static ngx_int_t

‌ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)

{

    ngx_http_core_loc_conf_t  *clcf;

    if (deny) {

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "access forbidden by rule");

        }

        return NGX_HTTP_FORBIDDEN;

    }

    return NGX_OK;

}

static char *

‌ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_access_loc_conf_t *alcf = conf;

    ngx_int_t                   rc;

    ngx_uint_t                  all;

    ngx_str_t                  *value;

    ngx_cidr_t                  cidr;

    ngx_http_access_rule_t     *rule;

#if (NGX_HAVE_INET6)

    ngx_http_access_rule6_t    *rule6;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    ngx_http_access_rule_un_t  *rule_un;

#endif

    ngx_memzero(&cidr, sizeof(ngx_cidr_t));

    value = cf->args->elts;

    all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);

    if (!all) {

#if (NGX_HAVE_UNIX_DOMAIN)

        if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) {

            cidr.family = AF_UNIX;

            rc = NGX_OK;

        } else {

            rc = ngx_ptocidr(&value[1], &cidr);

        }

#else

        rc = ngx_ptocidr(&value[1], &cidr);

#endif

        if (rc == NGX_ERROR) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                         "invalid parameter \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        if (rc == NGX_DONE) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                         "low address bits of %V are meaningless", &value[1]);

        }

    }

    if (cidr.family == AF_INET || all) {

        if (alcf->rules == NULL) {

            alcf->rules = ngx_array_create(cf->pool, 4,

                                           sizeof(ngx_http_access_rule_t));

            if (alcf->rules == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        rule = ngx_array_push(alcf->rules);

        if (rule == NULL) {

            return NGX_CONF_ERROR;

        }

        rule->mask = cidr.u.in.mask;

        rule->addr = cidr.u.in.addr;

        rule->deny = (value[0].data[0] == 'd') ? 1 : 0;

    }

#if (NGX_HAVE_INET6)

    if (cidr.family == AF_INET6 || all) {

        if (alcf->rules6 == NULL) {

            alcf->rules6 = ngx_array_create(cf->pool, 4,

                                            sizeof(ngx_http_access_rule6_t));

            if (alcf->rules6 == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        rule6 = ngx_array_push(alcf->rules6);

        if (rule6 == NULL) {

            return NGX_CONF_ERROR;

        }

        rule6->mask = cidr.u.in6.mask;

        rule6->addr = cidr.u.in6.addr;

        rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;

    }

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

    if (cidr.family == AF_UNIX || all) {

        if (alcf->rules_un == NULL) {

            alcf->rules_un = ngx_array_create(cf->pool, 1,

                                            sizeof(ngx_http_access_rule_un_t));

            if (alcf->rules_un == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        rule_un = ngx_array_push(alcf->rules_un);

        if (rule_un == NULL) {

            return NGX_CONF_ERROR;

        }

        rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;

    }

#endif

    return NGX_CONF_OK;

}

static void *

‌ngx_http_access_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_access_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    return conf;

}

static char *

‌ngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_access_loc_conf_t  *prev = parent;

    ngx_http_access_loc_conf_t  *conf = child;

    if (conf->rules == NULL

#if (NGX_HAVE_INET6)

        && conf->rules6 == NULL

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        && conf->rules_un == NULL

#endif

    ) {

        conf->rules = prev->rules;

#if (NGX_HAVE_INET6)

        conf->rules6 = prev->rules6;

#endif

#if (NGX_HAVE_UNIX_DOMAIN)

        conf->rules_un = prev->rules_un;

#endif

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_access_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_ACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_access_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_addition_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_addition_commands

ngx_http_addition_filter_module

ngx_http_addition_filter_module_ctx

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

ngx_http_addition_conf_t

ngx_http_addition_ctx_t

Functions defined

ngx_http_addition_body_filter

ngx_http_addition_create_conf

ngx_http_addition_filter_init

ngx_http_addition_header_filter

ngx_http_addition_merge_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_str_t     before_body;

    ngx_str_t     after_body;

    ngx_hash_t    types;

    ngx_array_t  *types_keys;

‌} ngx_http_addition_conf_t;

typedef struct {

    ngx_uint_t    before_body_sent;

‌} ngx_http_addition_ctx_t;

static void *ngx_http_addition_create_conf(ngx_conf_t *cf);

static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_addition_commands[] = {

    { ngx_string("add_before_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_addition_conf_t, before_body),

      NULL },

    { ngx_string("add_after_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_addition_conf_t, after_body),

      NULL },

    { ngx_string("addition_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_addition_conf_t, types_keys),

      &ngx_http_html_default_types[0] },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_addition_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_addition_filter_init,         /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_addition_create_conf,         /* create location configuration */

    ngx_http_addition_merge_conf           /* merge location configuration */

};

‌ngx_module_t  ngx_http_addition_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_addition_filter_module_ctx,  /* module context */

    ngx_http_addition_commands,            /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_addition_header_filter(ngx_http_request_t *r)

{

    ngx_http_addition_ctx_t   *ctx;

    ngx_http_addition_conf_t  *conf;

    if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {

        return ngx_http_next_header_filter(r);

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);

    if (conf->before_body.len == 0 && conf->after_body.len == 0) {

        return ngx_http_next_header_filter(r);

    }

    if (ngx_http_test_content_type(r, &conf->types) == NULL) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);

    ngx_http_clear_content_length(r);

    ngx_http_clear_accept_ranges(r);

    ngx_http_weak_etag(r);

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_int_t                  rc;

    ngx_uint_t                 last;

    ngx_chain_t               *cl;

    ngx_http_request_t        *sr;

    ngx_http_addition_ctx_t   *ctx;

    ngx_http_addition_conf_t  *conf;

    if (in == NULL || r->header_only) {

        return ngx_http_next_body_filter(r, in);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);

    if (ctx == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);

    if (!ctx->before_body_sent) {

        ctx->before_body_sent = 1;

        if (conf->before_body.len) {

            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)

                != NGX_OK)

            {

                return NGX_ERROR;

            }

        }

    }

    if (conf->after_body.len == 0) {

        ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);

        return ngx_http_next_body_filter(r, in);

    }

    last = 0;

    for (cl = in; cl; cl = cl->next) {

        if (cl->buf->last_buf) {

            cl->buf->last_buf = 0;

            cl->buf->sync = 1;

            last = 1;

        }

    }

    rc = ngx_http_next_body_filter(r, in);

    if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {

        return rc;

    }

    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);

    return ngx_http_send_special(r, NGX_HTTP_LAST);

}

static ngx_int_t

‌ngx_http_addition_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_addition_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_addition_body_filter;

    return NGX_OK;

}

static void *

‌ngx_http_addition_create_conf(ngx_conf_t *cf)

{

    ngx_http_addition_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->before_body = { 0, NULL };

     *     conf->after_body = { 0, NULL };

     *     conf->types = { NULL };

     *     conf->types_keys = NULL;

     */

    return conf;

}

static char *

‌ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_addition_conf_t *prev = parent;

    ngx_http_addition_conf_t *conf = child;

    ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");

    ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_html_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_auth_basic_module.c - nginx-1.7.9

Global variables defined

ngx_http_auth_basic_commands

ngx_http_auth_basic_module

ngx_http_auth_basic_module_ctx

Data types defined

ngx_http_auth_basic_ctx_t

ngx_http_auth_basic_loc_conf_t

Functions defined

ngx_http_auth_basic_close

ngx_http_auth_basic_create_loc_conf

ngx_http_auth_basic_crypt_handler

ngx_http_auth_basic_handler

ngx_http_auth_basic_init

ngx_http_auth_basic_merge_loc_conf

ngx_http_auth_basic_set_realm

ngx_http_auth_basic_user_file

Macros defined

NGX_HTTP_AUTH_BUF_SIZE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_crypt.h>

‌#define NGX_HTTP_AUTH_BUF_SIZE  2048

typedef struct {

    ngx_str_t                 passwd;

‌} ngx_http_auth_basic_ctx_t;

typedef struct {

    ngx_http_complex_value_t  *realm;

    ngx_http_complex_value_t   user_file;

‌} ngx_http_auth_basic_loc_conf_t;

static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,

    ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm);

static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,

    ngx_str_t *realm);

static void ngx_http_auth_basic_close(ngx_file_t *file);

static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);

static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_auth_basic_commands[] = {

    { ngx_string("auth_basic"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF

                        |NGX_CONF_TAKE1,

      ngx_http_set_complex_value_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_auth_basic_loc_conf_t, realm),

      NULL },

    { ngx_string("auth_basic_user_file"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF

                        |NGX_CONF_TAKE1,

      ngx_http_auth_basic_user_file,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_auth_basic_loc_conf_t, user_file),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_auth_basic_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_auth_basic_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_auth_basic_create_loc_conf,   /* create location configuration */

    ngx_http_auth_basic_merge_loc_conf     /* merge location configuration */

};

‌ngx_module_t  ngx_http_auth_basic_module = {

    NGX_MODULE_V1,

    &ngx_http_auth_basic_module_ctx,       /* module context */

    ngx_http_auth_basic_commands,          /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_auth_basic_handler(ngx_http_request_t *r)

{

    off_t                            offset;

    ssize_t                          n;

    ngx_fd_t                         fd;

    ngx_int_t                        rc;

    ngx_err_t                        err;

    ngx_str_t                        pwd, realm, user_file;

    ngx_uint_t                       i, level, login, left, passwd;

    ngx_file_t                       file;

    ngx_http_auth_basic_ctx_t       *ctx;

    ngx_http_auth_basic_loc_conf_t  *alcf;

    u_char                           buf[NGX_HTTP_AUTH_BUF_SIZE];

    enum {

        sw_login,

        sw_passwd,

        sw_skip

    } state;

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);

    if (alcf->realm == NULL || alcf->user_file.value.data == NULL) {

        return NGX_DECLINED;

    }

    if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {

        return NGX_ERROR;

    }

    if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {

        return NGX_DECLINED;

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);

    if (ctx) {

        return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd,

                                                 &realm);

    }

    rc = ngx_http_auth_basic_user(r);

    if (rc == NGX_DECLINED) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "no user/password was provided for basic authentication");

        return ngx_http_auth_basic_set_realm(r, &realm);

    }

    if (rc == NGX_ERROR) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {

        return NGX_ERROR;

    }

    fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

    if (fd == NGX_INVALID_FILE) {

        err = ngx_errno;

        if (err == NGX_ENOENT) {

            level = NGX_LOG_ERR;

            rc = NGX_HTTP_FORBIDDEN;

        } else {

            level = NGX_LOG_CRIT;

            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_log_error(level, r->connection->log, err,

                      ngx_open_file_n " \"%s\" failed", user_file.data);

        return rc;

    }

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.fd = fd;

    file.name = user_file;

    file.log = r->connection->log;

    state = sw_login;

    passwd = 0;

    login = 0;

    left = 0;

    offset = 0;

    for ( ;; ) {

        i = left;

        n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,

                          offset);

        if (n == NGX_ERROR) {

            ngx_http_auth_basic_close(&file);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        if (n == 0) {

            break;

        }

        for (i = left; i < left + n; i++) {

            switch (state) {

            case sw_login:

                if (login == 0) {

                    if (buf[i] == '#' || buf[i] == CR) {

                        state = sw_skip;

                        break;

                    }

                    if (buf[i] == LF) {

                        break;

                    }

                }

                if (buf[i] != r->headers_in.user.data[login]) {

                    state = sw_skip;

                    break;

                }

                if (login == r->headers_in.user.len) {

                    state = sw_passwd;

                    passwd = i + 1;

                }

                login++;

                break;

            case sw_passwd:

                if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {

                    buf[i] = '\0';

                    ngx_http_auth_basic_close(&file);

                    pwd.len = i - passwd;

                    pwd.data = &buf[passwd];

                    return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,

                                                             &realm);

                }

                break;

            case sw_skip:

                if (buf[i] == LF) {

                    state = sw_login;

                    login = 0;

                }

                break;

            }

        }

        if (state == sw_passwd) {

            left = left + n - passwd;

            ngx_memmove(buf, &buf[passwd], left);

            passwd = 0;

        } else {

            left = 0;

        }

        offset += n;

    }

    ngx_http_auth_basic_close(&file);

    if (state == sw_passwd) {

        pwd.len = i - passwd;

        pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);

        if (pwd.data == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);

        return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm);

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "user \"%V\" was not found in \"%V\"",

                  &r->headers_in.user, &user_file);

    return ngx_http_auth_basic_set_realm(r, &realm);

}

static ngx_int_t

‌ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,

    ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)

{

    ngx_int_t   rc;

    u_char     *encrypted;

    rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,

                   &encrypted);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "rc: %d user: \"%V\" salt: \"%s\"",

                   rc, &r->headers_in.user, passwd->data);

    if (rc == NGX_OK) {

        if (ngx_strcmp(encrypted, passwd->data) == 0) {

            return NGX_OK;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "encrypted: \"%s\"", encrypted);

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "user \"%V\": password mismatch",

                      &r->headers_in.user);

        return ngx_http_auth_basic_set_realm(r, realm);

    }

    if (rc == NGX_ERROR) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    /* rc == NGX_AGAIN */

    if (ctx == NULL) {

        ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));

        if (ctx == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);

        ctx->passwd.len = passwd->len;

        passwd->len++;

        ctx->passwd.data = ngx_pstrdup(r->pool, passwd);

        if (ctx->passwd.data == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    /* TODO: add mutex event */

    return rc;

}

static ngx_int_t

‌ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)

{

    size_t   len;

    u_char  *basic, *p;

    r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);

    if (r->headers_out.www_authenticate == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    len = sizeof("Basic realm=\"\"") - 1 + realm->len;

    basic = ngx_pnalloc(r->pool, len);

    if (basic == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);

    p = ngx_cpymem(p, realm->data, realm->len);

    *p = '"';

    r->headers_out.www_authenticate->hash = 1;

    ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");

    r->headers_out.www_authenticate->value.data = basic;

    r->headers_out.www_authenticate->value.len = len;

    return NGX_HTTP_UNAUTHORIZED;

}

static void

‌ngx_http_auth_basic_close(ngx_file_t *file)

{

    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", file->name.data);

    }

}

static void *

‌ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_auth_basic_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    return conf;

}

static char *

‌ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_auth_basic_loc_conf_t  *prev = parent;

    ngx_http_auth_basic_loc_conf_t  *conf = child;

    if (conf->realm == NULL) {

        conf->realm = prev->realm;

    }

    if (conf->user_file.value.data == NULL) {

        conf->user_file = prev->user_file;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_auth_basic_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_ACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_auth_basic_handler;

    return NGX_OK;

}

static char *

‌ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_auth_basic_loc_conf_t *alcf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    if (alcf->user_file.value.data) {

        return "is duplicate";

    }

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &alcf->user_file;

    ccv.zero = 1;

    ccv.conf_prefix = 1;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_auth_request_module.c - nginx-1.7.9

Global variables defined

ngx_http_auth_request_commands

ngx_http_auth_request_module

ngx_http_auth_request_module_ctx

Data types defined

ngx_http_auth_request_conf_t

ngx_http_auth_request_ctx_t

ngx_http_auth_request_variable_t

Functions defined

ngx_http_auth_request

ngx_http_auth_request_create_conf

ngx_http_auth_request_done

ngx_http_auth_request_handler

ngx_http_auth_request_init

ngx_http_auth_request_merge_conf

ngx_http_auth_request_set

ngx_http_auth_request_set_variables

ngx_http_auth_request_variable

Source code

/*

* Copyright (C) Maxim Dounin

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_str_t                 uri;

    ngx_array_t              *vars;

‌} ngx_http_auth_request_conf_t;

typedef struct {

    ngx_uint_t                done;

    ngx_uint_t                status;

    ngx_http_request_t       *subrequest;

‌} ngx_http_auth_request_ctx_t;

typedef struct {

    ngx_int_t                 index;

    ngx_http_complex_value_t  value;

    ngx_http_set_variable_pt  set_handler;

‌} ngx_http_auth_request_variable_t;

static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,

    void *data, ngx_int_t rc);

static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,

    ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);

static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);

static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);

static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_auth_request_commands[] = {

    { ngx_string("auth_request"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_auth_request,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("auth_request_set"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_http_auth_request_set,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_auth_request_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_auth_request_init,            /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_auth_request_create_conf,     /* create location configuration */

    ngx_http_auth_request_merge_conf       /* merge location configuration */

};

‌ngx_module_t  ngx_http_auth_request_module = {

    NGX_MODULE_V1,

    &ngx_http_auth_request_module_ctx,     /* module context */

    ngx_http_auth_request_commands,        /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_auth_request_handler(ngx_http_request_t *r)

{

    ngx_table_elt_t               *h, *ho;

    ngx_http_request_t            *sr;

    ngx_http_post_subrequest_t    *ps;

    ngx_http_auth_request_ctx_t   *ctx;

    ngx_http_auth_request_conf_t  *arcf;

    arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);

    if (arcf->uri.len == 0) {

        return NGX_DECLINED;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "auth request handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);

    if (ctx != NULL) {

        if (!ctx->done) {

            return NGX_AGAIN;

        }

        /*

         * as soon as we are done - explicitly set variables to make

         * sure they will be available after internal redirects

         */

        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {

            return NGX_ERROR;

        }

        /* return appropriate status */

        if (ctx->status == NGX_HTTP_FORBIDDEN) {

            return ctx->status;

        }

        if (ctx->status == NGX_HTTP_UNAUTHORIZED) {

            sr = ctx->subrequest;

            h = sr->headers_out.www_authenticate;

            if (!h && sr->upstream) {

                h = sr->upstream->headers_in.www_authenticate;

            }

            if (h) {

                ho = ngx_list_push(&r->headers_out.headers);

                if (ho == NULL) {

                    return NGX_ERROR;

                }

                *ho = *h;

                r->headers_out.www_authenticate = ho;

            }

            return ctx->status;

        }

        if (ctx->status >= NGX_HTTP_OK

            && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)

        {

            return NGX_OK;

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "auth request unexpected status: %d", ctx->status);

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));

    if (ps == NULL) {

        return NGX_ERROR;

    }

    ps->handler = ngx_http_auth_request_done;

    ps->data = ctx;

    if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,

                            NGX_HTTP_SUBREQUEST_WAITED)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    /*

     * allocate fake request body to avoid attempts to read it and to make

     * sure real body file (if already read) won't be closed by upstream

     */

    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

    if (sr->request_body == NULL) {

        return NGX_ERROR;

    }

    sr->header_only = 1;

    ctx->subrequest = sr;

    ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);

    return NGX_AGAIN;

}

static ngx_int_t

‌ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)

{

    ngx_http_auth_request_ctx_t   *ctx = data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "auth request done s:%d", r->headers_out.status);

    ctx->done = 1;

    ctx->status = r->headers_out.status;

    return rc;

}

static ngx_int_t

‌ngx_http_auth_request_set_variables(ngx_http_request_t *r,

    ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)

{

    ngx_str_t                          val;

    ngx_http_variable_t               *v;

    ngx_http_variable_value_t         *vv;

    ngx_http_auth_request_variable_t  *av, *last;

    ngx_http_core_main_conf_t         *cmcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "auth request set variables");

    if (arcf->vars == NULL) {

        return NGX_OK;

    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    v = cmcf->variables.elts;

    av = arcf->vars->elts;

    last = av + arcf->vars->nelts;

    while (av < last) {

        /*

         * explicitly set new value to make sure it will be available after

         * internal redirects

         */

        vv = &r->variables[av->index];

        if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        vv->valid = 1;

        vv->not_found = 0;

        vv->data = val.data;

        vv->len = val.len;

        if (av->set_handler) {

            /*

             * set_handler only available in cmcf->variables_keys, so we store

             * it explicitly

             */

            av->set_handler(r, vv, v[av->index].data);

        }

        av++;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_auth_request_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "auth request variable");

    v->not_found = 1;

    return NGX_OK;

}

static void *

‌ngx_http_auth_request_create_conf(ngx_conf_t *cf)

{

    ngx_http_auth_request_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->uri = { 0, NULL };

     */

    conf->vars = NGX_CONF_UNSET_PTR;

    return conf;

}

static char *

‌ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_auth_request_conf_t *prev = parent;

    ngx_http_auth_request_conf_t *conf = child;

    ngx_conf_merge_str_value(conf->uri, prev->uri, "");

    ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_auth_request_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_ACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_auth_request_handler;

    return NGX_OK;

}

static char *

‌ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_auth_request_conf_t *arcf = conf;

    ngx_str_t        *value;

    if (arcf->uri.data != NULL) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        arcf->uri.len = 0;

        arcf->uri.data = (u_char *) "";

        return NGX_CONF_OK;

    }

    arcf->uri = value[1];

    return NGX_CONF_OK;

}

static char *

‌ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_auth_request_conf_t *arcf = conf;

    ngx_str_t                         *value;

    ngx_http_variable_t               *v;

    ngx_http_auth_request_variable_t  *av;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (value[1].data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    value[1].len--;

    value[1].data++;

    if (arcf->vars == NGX_CONF_UNSET_PTR) {

        arcf->vars = ngx_array_create(cf->pool, 1,

                                      sizeof(ngx_http_auth_request_variable_t));

        if (arcf->vars == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    av = ngx_array_push(arcf->vars);

    if (av == NULL) {

        return NGX_CONF_ERROR;

    }

    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);

    if (v == NULL) {

        return NGX_CONF_ERROR;

    }

    av->index = ngx_http_get_variable_index(cf, &value[1]);

    if (av->index == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    if (v->get_handler == NULL) {

        v->get_handler = ngx_http_auth_request_variable;

        v->data = (uintptr_t) av;

    }

    av->set_handler = v->set_handler;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &av->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_autoindex_module.c - nginx-1.7.9

Global variables defined

ngx_http_autoindex_commands

ngx_http_autoindex_format

ngx_http_autoindex_module

ngx_http_autoindex_module_ctx

Data types defined

ngx_http_autoindex_entry_t

ngx_http_autoindex_loc_conf_t

Functions defined

ngx_http_autoindex_cmp_entries

ngx_http_autoindex_create_loc_conf

ngx_http_autoindex_error

ngx_http_autoindex_handler

ngx_http_autoindex_html

ngx_http_autoindex_init

ngx_http_autoindex_json

ngx_http_autoindex_jsonp_callback

ngx_http_autoindex_merge_loc_conf

ngx_http_autoindex_xml

Macros defined

NGX_HTTP_AUTOINDEX_HTML

NGX_HTTP_AUTOINDEX_JSON

NGX_HTTP_AUTOINDEX_JSONP

NGX_HTTP_AUTOINDEX_NAME_LEN

NGX_HTTP_AUTOINDEX_PREALLOCATE

NGX_HTTP_AUTOINDEX_XML

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#if 0

typedef struct {

    ngx_buf_t     *buf;

    size_t         size;

    ngx_pool_t    *pool;

    size_t         alloc_size;

    ngx_chain_t  **last_out;

} ngx_http_autoindex_ctx_t;

#endif

typedef struct {

    ngx_str_t      name;

    size_t         utf_len;

    size_t         escape;

    size_t         escape_html;

    unsigned       dir:1;

    unsigned       file:1;

    time_t         mtime;

    off_t          size;

‌} ngx_http_autoindex_entry_t;

typedef struct {

    ngx_flag_t     enable;

    ngx_uint_t     format;

    ngx_flag_t     localtime;

    ngx_flag_t     exact_size;

‌} ngx_http_autoindex_loc_conf_t;

‌#define NGX_HTTP_AUTOINDEX_HTML         0

‌#define NGX_HTTP_AUTOINDEX_JSON         1

‌#define NGX_HTTP_AUTOINDEX_JSONP        2

‌#define NGX_HTTP_AUTOINDEX_XML          3

‌#define NGX_HTTP_AUTOINDEX_PREALLOCATE  50

‌#define NGX_HTTP_AUTOINDEX_NAME_LEN     50

static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r,

    ngx_array_t *entries);

static ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r,

    ngx_array_t *entries, ngx_str_t *callback);

static ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r,

    ngx_str_t *callback);

static ngx_buf_t *ngx_http_autoindex_xml(ngx_http_request_t *r,

    ngx_array_t *entries);

static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,

    const void *two);

static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,

    ngx_dir_t *dir, ngx_str_t *name);

static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);

static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

‌static ngx_conf_enum_t  ngx_http_autoindex_format[] = {

    { ngx_string("html"), NGX_HTTP_AUTOINDEX_HTML },

    { ngx_string("json"), NGX_HTTP_AUTOINDEX_JSON },

    { ngx_string("jsonp"), NGX_HTTP_AUTOINDEX_JSONP },

    { ngx_string("xml"), NGX_HTTP_AUTOINDEX_XML },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_http_autoindex_commands[] = {

    { ngx_string("autoindex"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_autoindex_loc_conf_t, enable),

      NULL },

    { ngx_string("autoindex_format"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_autoindex_loc_conf_t, format),

      &ngx_http_autoindex_format },

    { ngx_string("autoindex_localtime"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_autoindex_loc_conf_t, localtime),

      NULL },

    { ngx_string("autoindex_exact_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_autoindex_loc_conf_t, exact_size),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_autoindex_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_autoindex_init,               /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_autoindex_create_loc_conf,    /* create location configuration */

    ngx_http_autoindex_merge_loc_conf      /* merge location configuration */

};

‌ngx_module_t  ngx_http_autoindex_module = {

    NGX_MODULE_V1,

    &ngx_http_autoindex_module_ctx,        /* module context */

    ngx_http_autoindex_commands,           /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_autoindex_handler(ngx_http_request_t *r)

{

    u_char                         *last, *filename;

    size_t                          len, allocated, root;

    ngx_err_t                       err;

    ngx_buf_t                      *b;

    ngx_int_t                       rc;

    ngx_str_t                       path, callback;

    ngx_dir_t                       dir;

    ngx_uint_t                      level, format;

    ngx_pool_t                     *pool;

    ngx_chain_t                     out;

    ngx_array_t                     entries;

    ngx_http_autoindex_entry_t     *entry;

    ngx_http_autoindex_loc_conf_t  *alcf;

    if (r->uri.data[r->uri.len - 1] != '/') {

        return NGX_DECLINED;

    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_DECLINED;

    }

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);

    if (!alcf->enable) {

        return NGX_DECLINED;

    }

    /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */

    last = ngx_http_map_uri_to_path(r, &path, &root,

                                    NGX_HTTP_AUTOINDEX_PREALLOCATE);

    if (last == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    allocated = path.len;

    path.len = last - path.data;

    if (path.len > 1) {

        path.len--;

    }

    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http autoindex: \"%s\"", path.data);

    format = alcf->format;

    if (format == NGX_HTTP_AUTOINDEX_JSONP) {

        if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {

            return NGX_HTTP_BAD_REQUEST;

        }

        if (callback.len == 0) {

            format = NGX_HTTP_AUTOINDEX_JSON;

        }

    }

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {

        err = ngx_errno;

        if (err == NGX_ENOENT

            || err == NGX_ENOTDIR

            || err == NGX_ENAMETOOLONG)

        {

            level = NGX_LOG_ERR;

            rc = NGX_HTTP_NOT_FOUND;

        } else if (err == NGX_EACCES) {

            level = NGX_LOG_ERR;

            rc = NGX_HTTP_FORBIDDEN;

        } else {

            level = NGX_LOG_CRIT;

            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_log_error(level, r->connection->log, err,

                      ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;

    }

#if (NGX_SUPPRESS_WARN)

    /* MSVC thinks 'entries' may be used without having been initialized */

    ngx_memzero(&entries, sizeof(ngx_array_t));

#endif

    /* TODO: pool should be temporary pool */

    pool = r->pool;

    if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))

        != NGX_OK)

    {

        return ngx_http_autoindex_error(r, &dir, &path);

    }

    r->headers_out.status = NGX_HTTP_OK;

    switch (format) {

    case NGX_HTTP_AUTOINDEX_JSON:

        ngx_str_set(&r->headers_out.content_type, "application/json");

        break;

    case NGX_HTTP_AUTOINDEX_JSONP:

        ngx_str_set(&r->headers_out.content_type, "application/javascript");

        break;

    case NGX_HTTP_AUTOINDEX_XML:

        ngx_str_set(&r->headers_out.content_type, "text/xml");

        ngx_str_set(&r->headers_out.charset, "utf-8");

        break;

    default: /* NGX_HTTP_AUTOINDEX_HTML */

        ngx_str_set(&r->headers_out.content_type, "text/html");

        break;

    }

    r->headers_out.content_type_len = r->headers_out.content_type.len;

    r->headers_out.content_type_lowcase = NULL;

    rc = ngx_http_send_header(r);

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

        if (ngx_close_dir(&dir) == NGX_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                          ngx_close_dir_n " \"%V\" failed", &path);

        }

        return rc;

    }

    filename = path.data;

    filename[path.len] = '/';

    for ( ;; ) {

        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {

            err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {

                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,

                              ngx_read_dir_n " \"%V\" failed", &path);

                return ngx_http_autoindex_error(r, &dir, &path);

            }

            break;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http autoindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (ngx_de_name(&dir)[0] == '.') {

            continue;

        }

        if (!dir.valid_info) {

            /* 1 byte for '/' and 1 byte for terminating '\0' */

            if (path.len + 1 + len + 1 > allocated) {

                allocated = path.len + 1 + len + 1

                                     + NGX_HTTP_AUTOINDEX_PREALLOCATE;

                filename = ngx_pnalloc(pool, allocated);

                if (filename == NULL) {

                    return ngx_http_autoindex_error(r, &dir, &path);

                }

                last = ngx_cpystrn(filename, path.data, path.len + 1);

                *last++ = '/';

            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {

                err = ngx_errno;

                if (err != NGX_ENOENT && err != NGX_ELOOP) {

                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,

                                  ngx_de_info_n " \"%s\" failed", filename);

                    if (err == NGX_EACCES) {

                        continue;

                    }

                    return ngx_http_autoindex_error(r, &dir, &path);

                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {

                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                                  ngx_de_link_info_n " \"%s\" failed",

                                  filename);

                    return ngx_http_autoindex_error(r, &dir, &path);

                }

            }

        }

        entry = ngx_array_push(&entries);

        if (entry == NULL) {

            return ngx_http_autoindex_error(r, &dir, &path);

        }

        entry->name.len = len;

        entry->name.data = ngx_pnalloc(pool, len + 1);

        if (entry->name.data == NULL) {

            return ngx_http_autoindex_error(r, &dir, &path);

        }

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);

        entry->dir = ngx_de_is_dir(&dir);

        entry->file = ngx_de_is_file(&dir);

        entry->mtime = ngx_de_mtime(&dir);

        entry->size = ngx_de_size(&dir);

    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                      ngx_close_dir_n " \"%V\" failed", &path);

    }

    if (entries.nelts > 1) {

        ngx_qsort(entries.elts, (size_t) entries.nelts,

                  sizeof(ngx_http_autoindex_entry_t),

                  ngx_http_autoindex_cmp_entries);

    }

    switch (format) {

    case NGX_HTTP_AUTOINDEX_JSON:

        b = ngx_http_autoindex_json(r, &entries, NULL);

        break;

    case NGX_HTTP_AUTOINDEX_JSONP:

        b = ngx_http_autoindex_json(r, &entries, &callback);

        break;

    case NGX_HTTP_AUTOINDEX_XML:

        b = ngx_http_autoindex_xml(r, &entries);

        break;

    default: /* NGX_HTTP_AUTOINDEX_HTML */

        b = ngx_http_autoindex_html(r, &entries);

        break;

    }

    if (b == NULL) {

        return NGX_ERROR;

    }

    /* TODO: free temporary pool */

    if (r == r->main) {

        b->last_buf = 1;

    }

    b->last_in_chain = 1;

    out.buf = b;

    out.next = NULL;

    return ngx_http_output_filter(r, &out);

}

static ngx_buf_t *

‌ngx_http_autoindex_html(ngx_http_request_t *r, ngx_array_t *entries)

{

    u_char                         *last, scale;

    off_t                           length;

    size_t                          len, char_len, escape_html;

    ngx_tm_t                        tm;

    ngx_buf_t                      *b;

    ngx_int_t                       size;

    ngx_uint_t                      i, utf8;

    ngx_time_t                     *tp;

    ngx_http_autoindex_entry_t     *entry;

    ngx_http_autoindex_loc_conf_t  *alcf;

    static u_char  title[] =

        "<html>" CRLF

        "<head><title>Index of "

    ;

    static u_char  header[] =

        "</title></head>" CRLF

        "<body bgcolor=\"white\">" CRLF

        "<h1>Index of "

    ;

    static u_char  tail[] =

        "</body>" CRLF

        "</html>" CRLF

    ;

    static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",

                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    if (r->headers_out.charset.len == 5

        && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)

           == 0)

    {

        utf8 = 1;

    } else {

        utf8 = 0;

    }

    escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);

    len = sizeof(title) - 1

          + r->uri.len + escape_html

          + sizeof(header) - 1

          + r->uri.len + escape_html

          + sizeof("</h1>") - 1

          + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1

          + sizeof("</pre><hr>") - 1

          + sizeof(tail) - 1;

    entry = entries->elts;

    for (i = 0; i < entries->nelts; i++) {

        entry[i].escape = 2 * ngx_escape_uri(NULL, entry[i].name.data,

                                             entry[i].name.len,

                                             NGX_ESCAPE_URI_COMPONENT);

        entry[i].escape_html = ngx_escape_html(NULL, entry[i].name.data,

                                               entry[i].name.len);

        if (utf8) {

            entry[i].utf_len = ngx_utf8_length(entry[i].name.data,

                                               entry[i].name.len);

        } else {

            entry[i].utf_len = entry[i].name.len;

        }

        len += sizeof("<a href=\"") - 1

            + entry[i].name.len + entry[i].escape

            + 1                                          /* 1 is for "/" */

            + sizeof("\">") - 1

            + entry[i].name.len - entry[i].utf_len

            + entry[i].escape_html

            + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2

            + sizeof("</a>") - 1

            + sizeof(" 28-Sep-1970 12:00 ") - 1

            + 20                                         /* the file size */

            + 2;

    }

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NULL;

    }

    b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);

    if (escape_html) {

        b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);

        b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);

        b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);

    } else {

        b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);

        b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);

        b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);

    }

    b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);

    b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,

                         sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);

    tp = ngx_timeofday();

    for (i = 0; i < entries->nelts; i++) {

        b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);

        if (entry[i].escape) {

            ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,

                           NGX_ESCAPE_URI_COMPONENT);

            b->last += entry[i].name.len + entry[i].escape;

        } else {

            b->last = ngx_cpymem(b->last, entry[i].name.data,

                                 entry[i].name.len);

        }

        if (entry[i].dir) {

            *b->last++ = '/';

        }

        *b->last++ = '"';

        *b->last++ = '>';

        len = entry[i].utf_len;

        if (entry[i].name.len != len) {

            if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {

                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;

            } else {

                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;

            }

            last = b->last;

            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,

                                       char_len, entry[i].name.len + 1);

            if (entry[i].escape_html) {

                b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,

                                                     b->last - last);

            }

            last = b->last;

        } else {

            if (entry[i].escape_html) {

                if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {

                    char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;

                } else {

                    char_len = len;

                }

                b->last = (u_char *) ngx_escape_html(b->last,

                                                  entry[i].name.data, char_len);

                last = b->last;

            } else {

                b->last = ngx_cpystrn(b->last, entry[i].name.data,

                                      NGX_HTTP_AUTOINDEX_NAME_LEN + 1);

                last = b->last - 3;

            }

        }

        if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {

            b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);

        } else {

            if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {

                *b->last++ = '/';

                len++;

            }

            b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);

            if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {

                ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);

                b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;

            }

        }

        *b->last++ = ' ';

        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);

        b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",

                              tm.ngx_tm_mday,

                              months[tm.ngx_tm_mon - 1],

                              tm.ngx_tm_year,

                              tm.ngx_tm_hour,

                              tm.ngx_tm_min);

        if (alcf->exact_size) {

            if (entry[i].dir) {

                b->last = ngx_cpymem(b->last,  "                  -",

                                     sizeof("                  -") - 1);

            } else {

                b->last = ngx_sprintf(b->last, "%19O", entry[i].size);

            }

        } else {

            if (entry[i].dir) {

                b->last = ngx_cpymem(b->last,  "      -",

                                     sizeof("      -") - 1);

            } else {

                length = entry[i].size;

                if (length > 1024 * 1024 * 1024 - 1) {

                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));

                    if ((length % (1024 * 1024 * 1024))

                                                > (1024 * 1024 * 1024 / 2 - 1))

                    {

                        size++;

                    }

                    scale = 'G';

                } else if (length > 1024 * 1024 - 1) {

                    size = (ngx_int_t) (length / (1024 * 1024));

                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {

                        size++;

                    }

                    scale = 'M';

                } else if (length > 9999) {

                    size = (ngx_int_t) (length / 1024);

                    if (length % 1024 > 511) {

                        size++;

                    }

                    scale = 'K';

                } else {

                    size = (ngx_int_t) length;

                    scale = '\0';

                }

                if (scale) {

                    b->last = ngx_sprintf(b->last, "%6i%c", size, scale);

                } else {

                    b->last = ngx_sprintf(b->last, " %6i", size);

                }

            }

        }

        *b->last++ = CR;

        *b->last++ = LF;

    }

    b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);

    b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);

    return b;

}

static ngx_buf_t *

‌ngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries,

    ngx_str_t *callback)

{

    size_t                       len;

    ngx_buf_t                   *b;

    ngx_uint_t                   i;

    ngx_http_autoindex_entry_t  *entry;

    len = sizeof("[" CRLF CRLF "]") - 1;

    if (callback) {

        len += sizeof("/* callback */" CRLF "();") - 1 + callback->len;

    }

    entry = entries->elts;

    for (i = 0; i < entries->nelts; i++) {

        entry[i].escape = ngx_escape_json(NULL, entry[i].name.data,

                                          entry[i].name.len);

        len += sizeof("{  }," CRLF) - 1

            + sizeof("\"name\":\"\"") - 1

            + entry[i].name.len + entry[i].escape

            + sizeof(", \"type\":\"directory\"") - 1

            + sizeof(", \"mtime\":\"Wed, 31 Dec 1986 10:00:00 GMT\"") - 1;

        if (entry[i].file) {

            len += sizeof(", \"size\":") - 1 + NGX_OFF_T_LEN;

        }

    }

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NULL;

    }

    if (callback) {

        b->last = ngx_cpymem(b->last, "/* callback */" CRLF,

                             sizeof("/* callback */" CRLF) - 1);

        b->last = ngx_cpymem(b->last, callback->data, callback->len);

        *b->last++ = '(';

    }

    *b->last++ = '[';

    for (i = 0; i < entries->nelts; i++) {

        b->last = ngx_cpymem(b->last, CRLF "{ \"name\":\"",

                             sizeof(CRLF "{ \"name\":\"") - 1);

        if (entry[i].escape) {

            b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data,

                                                 entry[i].name.len);

        } else {

            b->last = ngx_cpymem(b->last, entry[i].name.data,

                                 entry[i].name.len);

        }

        b->last = ngx_cpymem(b->last, "\", \"type\":\"",

                             sizeof("\", \"type\":\"") - 1);

        if (entry[i].dir) {

            b->last = ngx_cpymem(b->last, "directory", sizeof("directory") - 1);

        } else if (entry[i].file) {

            b->last = ngx_cpymem(b->last, "file", sizeof("file") - 1);

        } else {

            b->last = ngx_cpymem(b->last, "other", sizeof("other") - 1);

        }

        b->last = ngx_cpymem(b->last, "\", \"mtime\":\"",

                             sizeof("\", \"mtime\":\"") - 1);

        b->last = ngx_http_time(b->last, entry[i].mtime);

        if (entry[i].file) {

            b->last = ngx_cpymem(b->last, "\", \"size\":",

                                 sizeof("\", \"size\":") - 1);

            b->last = ngx_sprintf(b->last, "%O", entry[i].size);

        } else {

            *b->last++ = '"';

        }

        b->last = ngx_cpymem(b->last, " },", sizeof(" },") - 1);

    }

    if (i > 0) {

        b->last--;  /* strip last comma */

    }

    b->last = ngx_cpymem(b->last, CRLF "]", sizeof(CRLF "]") - 1);

    if (callback) {

        *b->last++ = ')'; *b->last++ = ';';

    }

    return b;

}

static ngx_int_t

‌ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, ngx_str_t *callback)

{

    u_char      *p, c, ch;

    ngx_uint_t   i;

    if (ngx_http_arg(r, (u_char *) "callback", 8, callback) != NGX_OK) {

        callback->len = 0;

        return NGX_OK;

    }

    if (callback->len > 128) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent too long callback name: \"%V\"", callback);

        return NGX_DECLINED;

    }

    p = callback->data;

    for (i = 0; i < callback->len; i++) {

        ch = p[i];

        c = (u_char) (ch | 0x20);

        if (c >= 'a' && c <= 'z') {

            continue;

        }

        if ((ch >= '0' && ch <= '9') || ch == '_' || ch == '.') {

            continue;

        }

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "client sent invalid callback name: \"%V\"", callback);

        return NGX_DECLINED;

    }

    return NGX_OK;

}

static ngx_buf_t *

‌ngx_http_autoindex_xml(ngx_http_request_t *r, ngx_array_t *entries)

{

    size_t                          len;

    ngx_tm_t                        tm;

    ngx_buf_t                      *b;

    ngx_str_t                       type;

    ngx_uint_t                      i;

    ngx_http_autoindex_entry_t     *entry;

    static u_char  head[] = "<?xml version=\"1.0\"?>" CRLF "<list>" CRLF;

    static u_char  tail[] = "</list>" CRLF;

    len = sizeof(head) - 1 + sizeof(tail) - 1;

    entry = entries->elts;

    for (i = 0; i < entries->nelts; i++) {

        entry[i].escape = ngx_escape_html(NULL, entry[i].name.data,

                                          entry[i].name.len);

        len += sizeof("<directory></directory>" CRLF) - 1

            + entry[i].name.len + entry[i].escape

            + sizeof(" mtime=\"1986-12-31T10:00:00Z\"") - 1;

        if (entry[i].file) {

            len += sizeof(" size=\"\"") - 1 + NGX_OFF_T_LEN;

        }

    }

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NULL;

    }

    b->last = ngx_cpymem(b->last, head, sizeof(head) - 1);

    for (i = 0; i < entries->nelts; i++) {

        *b->last++ = '<';

        if (entry[i].dir) {

            ngx_str_set(&type, "directory");

        } else if (entry[i].file) {

            ngx_str_set(&type, "file");

        } else {

            ngx_str_set(&type, "other");

        }

        b->last = ngx_cpymem(b->last, type.data, type.len);

        b->last = ngx_cpymem(b->last, " mtime=\"", sizeof(" mtime=\"") - 1);

        ngx_gmtime(entry[i].mtime, &tm);

        b->last = ngx_sprintf(b->last, "%4d-%02d-%02dT%02d:%02d:%02dZ",

                              tm.ngx_tm_year, tm.ngx_tm_mon,

                              tm.ngx_tm_mday, tm.ngx_tm_hour,

                              tm.ngx_tm_min, tm.ngx_tm_sec);

        if (entry[i].file) {

            b->last = ngx_cpymem(b->last, "\" size=\"",

                                 sizeof("\" size=\"") - 1);

            b->last = ngx_sprintf(b->last, "%O", entry[i].size);

        }

        *b->last++ = '"'; *b->last++ = '>';

        if (entry[i].escape) {

            b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data,

                                                 entry[i].name.len);

        } else {

            b->last = ngx_cpymem(b->last, entry[i].name.data,

                                 entry[i].name.len);

        }

        *b->last++ = '<'; *b->last++ = '/';

        b->last = ngx_cpymem(b->last, type.data, type.len);

        *b->last++ = '>';

        *b->last++ = CR; *b->last++ = LF;

    }

    b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);

    return b;

}

static int ngx_libc_cdecl

‌ngx_http_autoindex_cmp_entries(const void *one, const void *two)

{

    ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;

    ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;

    if (first->dir && !second->dir) {

        /* move the directories to the start */

        return -1;

    }

    if (!first->dir && second->dir) {

        /* move the directories to the start */

        return 1;

    }

    return (int) ngx_strcmp(first->name.data, second->name.data);

}

#if 0

static ngx_buf_t *

ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)

{

    ngx_chain_t  *cl;

    if (ctx->buf) {

        if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {

            return ctx->buf;

        }

        ctx->size += ctx->buf->last - ctx->buf->pos;

    }

    ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);

    if (ctx->buf == NULL) {

        return NULL;

    }

    cl = ngx_alloc_chain_link(ctx->pool);

    if (cl == NULL) {

        return NULL;

    }

    cl->buf = ctx->buf;

    cl->next = NULL;

    *ctx->last_out = cl;

    ctx->last_out = &cl->next;

    return ctx->buf;

}

#endif

static ngx_int_t

‌ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)

{

    if (ngx_close_dir(dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                      ngx_close_dir_n " \"%V\" failed", name);

    }

    return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;

}

static void *

‌ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_autoindex_loc_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->enable = NGX_CONF_UNSET;

    conf->format = NGX_CONF_UNSET_UINT;

    conf->localtime = NGX_CONF_UNSET;

    conf->exact_size = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_autoindex_loc_conf_t *prev = parent;

    ngx_http_autoindex_loc_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_uint_value(conf->format, prev->format,

                              NGX_HTTP_AUTOINDEX_HTML);

    ngx_conf_merge_value(conf->localtime, prev->localtime, 0);

    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_autoindex_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_autoindex_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_browser_module.c - nginx-1.7.9

Global variables defined

ngx_http_browser_commands

ngx_http_browser_module

ngx_http_browser_module_ctx

ngx_http_browsers

ngx_http_modern_browser_masks

Data types defined

ngx_http_browser_conf_t

ngx_http_browser_variable_t

ngx_http_modern_browser_mask_t

ngx_http_modern_browser_t

Functions defined

ngx_http_ancient_browser

ngx_http_ancient_browser_value

ngx_http_browser

ngx_http_browser_add_variable

ngx_http_browser_create_conf

ngx_http_browser_merge_conf

ngx_http_browser_variable

ngx_http_modern_browser

ngx_http_modern_browser_sort

ngx_http_modern_browser_value

ngx_http_msie_variable

Macros defined

NGX_HTTP_ANCIENT_BROWSER

NGX_HTTP_MODERN_BROWSER

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

/*

* The module can check browser versions conforming to the following formats:

* X, X.X, X.X.X, and X.X.X.X.  The maximum values of each format may be

* 4000, 4000.99, 4000.99.99, and 4000.99.99.99.

*/

‌#define  NGX_HTTP_MODERN_BROWSER   0

‌#define  NGX_HTTP_ANCIENT_BROWSER  1

typedef struct {

    u_char                      browser[12];

    size_t                      skip;

    size_t                      add;

    u_char                      name[12];

‌} ngx_http_modern_browser_mask_t;

typedef struct {

    ngx_uint_t                  version;

    size_t                      skip;

    size_t                      add;

    u_char                      name[12];

‌} ngx_http_modern_browser_t;

typedef struct {

    ngx_str_t                   name;

    ngx_http_get_variable_pt    handler;

    uintptr_t                   data;

‌} ngx_http_browser_variable_t;

typedef struct {

    ngx_array_t                *modern_browsers;

    ngx_array_t                *ancient_browsers;

    ngx_http_variable_value_t  *modern_browser_value;

    ngx_http_variable_value_t  *ancient_browser_value;

    unsigned                    modern_unlisted_browsers:1;

    unsigned                    netscape4:1;

‌} ngx_http_browser_conf_t;

static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,

    ngx_http_browser_conf_t *cf);

static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf);

static void *ngx_http_browser_create_conf(ngx_conf_t *cf);

static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,

    const void *two);

static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_browser_commands[] = {

    { ngx_string("modern_browser"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_modern_browser,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ancient_browser"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_ancient_browser,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("modern_browser_value"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_modern_browser_value,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("ancient_browser_value"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_ancient_browser_value,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_browser_module_ctx = {

    ngx_http_browser_add_variable,         /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_browser_create_conf,          /* create location configuration */

    ngx_http_browser_merge_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_browser_module = {

    NGX_MODULE_V1,

    &ngx_http_browser_module_ctx,          /* module context */

    ngx_http_browser_commands,             /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_modern_browser_mask_t  ngx_http_modern_browser_masks[] = {

    /* Opera must be the first browser to check */

    /*

     * "Opera/7.50 (X11; FreeBSD i386; U)  [en]"

     * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50  [en]"

     * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50  [en]"

     * "Opera/8.0 (Windows NT 5.1; U; ru)"

     * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"

     * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"

     */

    { "opera",

      0,

      sizeof("Opera ") - 1,

      "Opera"},

    /* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */

    { "msie",

      sizeof("Mozilla/4.0 (compatible; ") - 1,

      sizeof("MSIE ") - 1,

      "MSIE "},

    /*

     * "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"

     * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"

     * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206

     *              Firefox/0.8"

     * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)

     *              Gecko/20050511 Firefox/1.0.4"

     * "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729

     *              Firefox/1.5.0.5"

     */

    { "gecko",

      sizeof("Mozilla/5.0 (") - 1,

      sizeof("rv:") - 1,

      "rv:"},

    /*

     * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2

     *              (KHTML, like Gecko) Safari/125.7"

     * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413

     *              (KHTML, like Gecko) Safari/413"

     * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418

     *              (KHTML, like Gecko) Safari/417.9.3"

     * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8

     *              (KHTML, like Gecko) Safari/419.3"

     */

    { "safari",

      sizeof("Mozilla/5.0 (") - 1,

      sizeof("Safari/") - 1,

      "Safari/"},

    /*

     * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"

     * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"

     * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1

     *              (like Gecko)"

     */

    { "konqueror",

      sizeof("Mozilla/5.0 (compatible; ") - 1,

      sizeof("Konqueror/") - 1,

      "Konqueror/"},

    { "", 0, 0, "" }

};

‌static ngx_http_browser_variable_t  ngx_http_browsers[] = {

    { ngx_string("msie"), ngx_http_msie_variable, 0 },

    { ngx_string("modern_browser"), ngx_http_browser_variable,

          NGX_HTTP_MODERN_BROWSER },

    { ngx_string("ancient_browser"), ngx_http_browser_variable,

          NGX_HTTP_ANCIENT_BROWSER },

    { ngx_null_string, NULL, 0 }

};

static ngx_int_t

‌ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_uint_t                rc;

    ngx_http_browser_conf_t  *cf;

    cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);

    rc = ngx_http_browser(r, cf);

    if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {

        *v = *cf->modern_browser_value;

        return NGX_OK;

    }

    if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {

        *v = *cf->ancient_browser_value;

        return NGX_OK;

    }

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static ngx_uint_t

‌ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)

{

    size_t                      len;

    u_char                     *name, *ua, *last, c;

    ngx_str_t                  *ancient;

    ngx_uint_t                  i, version, ver, scale;

    ngx_http_modern_browser_t  *modern;

    if (r->headers_in.user_agent == NULL) {

        if (cf->modern_unlisted_browsers) {

            return NGX_HTTP_MODERN_BROWSER;

        }

        return NGX_HTTP_ANCIENT_BROWSER;

    }

    ua = r->headers_in.user_agent->value.data;

    len = r->headers_in.user_agent->value.len;

    last = ua + len;

    if (cf->modern_browsers) {

        modern = cf->modern_browsers->elts;

        for (i = 0; i < cf->modern_browsers->nelts; i++) {

            name = ua + modern[i].skip;

            if (name >= last) {

                continue;

            }

            name = (u_char *) ngx_strstr(name, modern[i].name);

            if (name == NULL) {

                continue;

            }

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "browser: \"%s\"", name);

            name += modern[i].add;

            if (name >= last) {

                continue;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "version: \"%ui\" \"%s\"", modern[i].version, name);

            version = 0;

            ver = 0;

            scale = 1000000;

            while (name < last) {

                c = *name++;

                if (c >= '0' && c <= '9') {

                    ver = ver * 10 + (c - '0');

                    continue;

                }

                if (c == '.') {

                    version += ver * scale;

                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                                   "version: \"%ui\" \"%ui\"",

                                   modern[i].version, version);

                    if (version > modern[i].version) {

                        return NGX_HTTP_MODERN_BROWSER;

                    }

                    ver = 0;

                    scale /= 100;

                    continue;

                }

                break;

            }

            version += ver * scale;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "version: \"%ui\" \"%ui\"",

                           modern[i].version, version);

            if (version >= modern[i].version) {

                return NGX_HTTP_MODERN_BROWSER;

            }

            return NGX_HTTP_ANCIENT_BROWSER;

        }

        if (!cf->modern_unlisted_browsers) {

            return NGX_HTTP_ANCIENT_BROWSER;

        }

    }

    if (cf->netscape4) {

        if (len > sizeof("Mozilla/4.72 ") - 1

            && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0

            && ua[8] > '0' && ua[8] < '5')

        {

            return NGX_HTTP_ANCIENT_BROWSER;

        }

    }

    if (cf->ancient_browsers) {

        ancient = cf->ancient_browsers->elts;

        for (i = 0; i < cf->ancient_browsers->nelts; i++) {

            if (len >= ancient[i].len

                && ngx_strstr(ua, ancient[i].data) != NULL)

            {

                return NGX_HTTP_ANCIENT_BROWSER;

            }

        }

    }

    if (cf->modern_unlisted_browsers) {

        return NGX_HTTP_MODERN_BROWSER;

    }

    return NGX_HTTP_ANCIENT_BROWSER;

}

static ngx_int_t

‌ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    if (r->headers_in.msie) {

        *v = ngx_http_variable_true_value;

        return NGX_OK;

    }

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_browser_add_variable(ngx_conf_t *cf)

{

    ngx_http_browser_variable_t   *var;

    ngx_http_variable_t           *v;

    for (var = ngx_http_browsers; var->name.len; var++) {

        v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE);

        if (v == NULL) {

            return NGX_ERROR;

        }

        v->get_handler = var->handler;

        v->data = var->data;

    }

    return NGX_OK;

}

static void *

‌ngx_http_browser_create_conf(ngx_conf_t *cf)

{

    ngx_http_browser_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->modern_browsers = NULL;

     *     conf->ancient_browsers = NULL;

     *     conf->modern_browser_value = NULL;

     *     conf->ancient_browser_value = NULL;

     *

     *     conf->modern_unlisted_browsers = 0;

     *     conf->netscape4 = 0;

     */

    return conf;

}

static char *

‌ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_browser_conf_t *prev = parent;

    ngx_http_browser_conf_t *conf = child;

    ngx_uint_t                  i, n;

    ngx_http_modern_browser_t  *browsers, *opera;

    /*

     * At the merge the skip field is used to store the browser slot,

     * it will be used in sorting and then will overwritten

     * with a real skip value.  The zero value means Opera.

     */

    if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {

        conf->modern_browsers = prev->modern_browsers;

        conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;

    } else if (conf->modern_browsers != NULL) {

        browsers = conf->modern_browsers->elts;

        for (i = 0; i < conf->modern_browsers->nelts; i++) {

            if (browsers[i].skip == 0) {

                goto found;

            }

        }

        /*

         * Opera may contain MSIE string, so if Opera was not enumerated

         * as modern browsers, then add it and set a unreachable version

         */

        opera = ngx_array_push(conf->modern_browsers);

        if (opera == NULL) {

            return NGX_CONF_ERROR;

        }

        opera->skip = 0;

        opera->version = 4001000000U;

        browsers = conf->modern_browsers->elts;

found:

        ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,

                  sizeof(ngx_http_modern_browser_t),

                  ngx_http_modern_browser_sort);

        for (i = 0; i < conf->modern_browsers->nelts; i++) {

             n = browsers[i].skip;

             browsers[i].skip = ngx_http_modern_browser_masks[n].skip;

             browsers[i].add = ngx_http_modern_browser_masks[n].add;

             (void) ngx_cpystrn(browsers[i].name,

                                ngx_http_modern_browser_masks[n].name, 12);

        }

    }

    if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {

        conf->ancient_browsers = prev->ancient_browsers;

        conf->netscape4 = prev->netscape4;

    }

    if (conf->modern_browser_value == NULL) {

        conf->modern_browser_value = prev->modern_browser_value;

    }

    if (conf->modern_browser_value == NULL) {

        conf->modern_browser_value = &ngx_http_variable_true_value;

    }

    if (conf->ancient_browser_value == NULL) {

        conf->ancient_browser_value = prev->ancient_browser_value;

    }

    if (conf->ancient_browser_value == NULL) {

        conf->ancient_browser_value = &ngx_http_variable_true_value;

    }

    return NGX_CONF_OK;

}

static int ngx_libc_cdecl

‌ngx_http_modern_browser_sort(const void *one, const void *two)

{

    ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;

    ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;

    return (first->skip - second->skip);

}

static char *

‌ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_browser_conf_t *bcf = conf;

    u_char                           c;

    ngx_str_t                       *value;

    ngx_uint_t                       i, n, version, ver, scale;

    ngx_http_modern_browser_t       *browser;

    ngx_http_modern_browser_mask_t  *mask;

    value = cf->args->elts;

    if (cf->args->nelts == 2) {

        if (ngx_strcmp(value[1].data, "unlisted") == 0) {

            bcf->modern_unlisted_browsers = 1;

            return NGX_CONF_OK;

        }

        return NGX_CONF_ERROR;

    }

    if (bcf->modern_browsers == NULL) {

        bcf->modern_browsers = ngx_array_create(cf->pool, 5,

                                            sizeof(ngx_http_modern_browser_t));

        if (bcf->modern_browsers == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    browser = ngx_array_push(bcf->modern_browsers);

    if (browser == NULL) {

        return NGX_CONF_ERROR;

    }

    mask = ngx_http_modern_browser_masks;

    for (n = 0; mask[n].browser[0] != '\0'; n++) {

        if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {

            goto found;

        }

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "unknown browser name \"%V\"", &value[1]);

    return NGX_CONF_ERROR;

found:

    /*

     * at this stage the skip field is used to store the browser slot,

     * it will be used in sorting in merge stage and then will overwritten

     * with a real value

     */

    browser->skip = n;

    version = 0;

    ver = 0;

    scale = 1000000;

    for (i = 0; i < value[2].len; i++) {

        c = value[2].data[i];

        if (c >= '0' && c <= '9') {

            ver = ver * 10 + (c - '0');

            continue;

        }

        if (c == '.') {

            version += ver * scale;

            ver = 0;

            scale /= 100;

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid browser version \"%V\"", &value[2]);

        return NGX_CONF_ERROR;

    }

    version += ver * scale;

    browser->version = version;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_browser_conf_t *bcf = conf;

    ngx_str_t   *value, *browser;

    ngx_uint_t   i;

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strcmp(value[i].data, "netscape4") == 0) {

            bcf->netscape4 = 1;

            continue;

        }

        if (bcf->ancient_browsers == NULL) {

            bcf->ancient_browsers = ngx_array_create(cf->pool, 4,

                                                     sizeof(ngx_str_t));

            if (bcf->ancient_browsers == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        browser = ngx_array_push(bcf->ancient_browsers);

        if (browser == NULL) {

            return NGX_CONF_ERROR;

        }

        *browser = value[i];

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_browser_conf_t *bcf = conf;

    ngx_str_t  *value;

    bcf->modern_browser_value = ngx_palloc(cf->pool,

                                           sizeof(ngx_http_variable_value_t));

    if (bcf->modern_browser_value == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    bcf->modern_browser_value->len = value[1].len;

    bcf->modern_browser_value->valid = 1;

    bcf->modern_browser_value->no_cacheable = 0;

    bcf->modern_browser_value->not_found = 0;

    bcf->modern_browser_value->data = value[1].data;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_browser_conf_t *bcf = conf;

    ngx_str_t  *value;

    bcf->ancient_browser_value = ngx_palloc(cf->pool,

                                            sizeof(ngx_http_variable_value_t));

    if (bcf->ancient_browser_value == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    bcf->ancient_browser_value->len = value[1].len;

    bcf->ancient_browser_value->valid = 1;

    bcf->ancient_browser_value->no_cacheable = 0;

    bcf->ancient_browser_value->not_found = 0;

    bcf->ancient_browser_value->data = value[1].data;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_charset_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_charset_default_types

ngx_http_charset_filter_commands

ngx_http_charset_filter_module

ngx_http_charset_filter_module_ctx

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

ngx_http_charset_conf_ctx_t

ngx_http_charset_ctx_t

ngx_http_charset_loc_conf_t

ngx_http_charset_main_conf_t

ngx_http_charset_recode_t

ngx_http_charset_t

ngx_http_charset_tables_t

Functions defined

ngx_http_add_charset

ngx_http_charset_body_filter

ngx_http_charset_create_loc_conf

ngx_http_charset_create_main_conf

ngx_http_charset_ctx

ngx_http_charset_get_buf

ngx_http_charset_get_buffer

ngx_http_charset_header_filter

ngx_http_charset_map

ngx_http_charset_map_block

ngx_http_charset_merge_loc_conf

ngx_http_charset_postconfiguration

ngx_http_charset_recode

ngx_http_charset_recode_from_utf8

ngx_http_charset_recode_to_utf8

ngx_http_destination_charset

ngx_http_get_charset

ngx_http_main_request_charset

ngx_http_set_charset

ngx_http_set_charset_slot

ngx_http_source_charset

Macros defined

NGX_HTML_ENTITY_LEN

NGX_HTTP_CHARSET_OFF

NGX_HTTP_CHARSET_VAR

NGX_HTTP_NO_CHARSET

NGX_UTF_LEN

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_CHARSET_OFF    -2

‌#define NGX_HTTP_NO_CHARSET     -3

‌#define NGX_HTTP_CHARSET_VAR    0x10000

/* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */

‌#define NGX_UTF_LEN             4

‌#define NGX_HTML_ENTITY_LEN     (sizeof("&#1114111;") - 1)

typedef struct {

    u_char                    **tables;

    ngx_str_t                   name;

    unsigned                    length:16;

    unsigned                    utf8:1;

‌} ngx_http_charset_t;

typedef struct {

    ngx_int_t                   src;

    ngx_int_t                   dst;

‌} ngx_http_charset_recode_t;

typedef struct {

    ngx_int_t                   src;

    ngx_int_t                   dst;

    u_char                     *src2dst;

    u_char                     *dst2src;

‌} ngx_http_charset_tables_t;

typedef struct {

    ngx_array_t                 charsets;       /* ngx_http_charset_t */

    ngx_array_t                 tables;         /* ngx_http_charset_tables_t */

    ngx_array_t                 recodes;        /* ngx_http_charset_recode_t */

‌} ngx_http_charset_main_conf_t;

typedef struct {

    ngx_int_t                   charset;

    ngx_int_t                   source_charset;

    ngx_flag_t                  override_charset;

    ngx_hash_t                  types;

    ngx_array_t                *types_keys;

‌} ngx_http_charset_loc_conf_t;

typedef struct {

    u_char                     *table;

    ngx_int_t                   charset;

    ngx_str_t                   charset_name;

    ngx_chain_t                *busy;

    ngx_chain_t                *free_bufs;

    ngx_chain_t                *free_buffers;

    size_t                      saved_len;

    u_char                      saved[NGX_UTF_LEN];

    unsigned                    length:16;

    unsigned                    from_utf8:1;

    unsigned                    to_utf8:1;

‌} ngx_http_charset_ctx_t;

typedef struct {

    ngx_http_charset_tables_t  *table;

    ngx_http_charset_t         *charset;

    ngx_uint_t                  characters;

‌} ngx_http_charset_conf_ctx_t;

static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,

    ngx_str_t *name);

static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,

    ngx_str_t *name);

static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,

    ngx_str_t *name);

static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);

static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,

    ngx_str_t *charset);

static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,

    ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);

static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);

static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,

    ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);

static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,

    ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);

static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,

    ngx_http_charset_ctx_t *ctx);

static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,

    ngx_http_charset_ctx_t *ctx, size_t size);

static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,

    void *conf);

static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);

static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);

‌ngx_str_t  ngx_http_charset_default_types[] = {

    ngx_string("text/html"),

    ngx_string("text/xml"),

    ngx_string("text/plain"),

    ngx_string("text/vnd.wap.wml"),

    ngx_string("application/javascript"),

    ngx_string("application/rss+xml"),

    ngx_null_string

};

‌static ngx_command_t  ngx_http_charset_filter_commands[] = {

    { ngx_string("charset"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF

                        |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_set_charset_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_charset_loc_conf_t, charset),

      NULL },

    { ngx_string("source_charset"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF

                        |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_set_charset_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_charset_loc_conf_t, source_charset),

      NULL },

    { ngx_string("override_charset"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF

                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_charset_loc_conf_t, override_charset),

      NULL },

    { ngx_string("charset_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_charset_loc_conf_t, types_keys),

      &ngx_http_charset_default_types[0] },

    { ngx_string("charset_map"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,

      ngx_http_charset_map_block,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_charset_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_charset_postconfiguration,    /* postconfiguration */

    ngx_http_charset_create_main_conf,     /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_charset_create_loc_conf,      /* create location configuration */

    ngx_http_charset_merge_loc_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_charset_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_charset_filter_module_ctx,   /* module context */

    ngx_http_charset_filter_commands,      /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_charset_header_filter(ngx_http_request_t *r)

{

    ngx_int_t                      charset, source_charset;

    ngx_str_t                      dst, src;

    ngx_http_charset_t            *charsets;

    ngx_http_charset_main_conf_t  *mcf;

    if (r == r->main) {

        charset = ngx_http_destination_charset(r, &dst);

    } else {

        charset = ngx_http_main_request_charset(r, &dst);

    }

    if (charset == NGX_ERROR) {

        return NGX_ERROR;

    }

    if (charset == NGX_DECLINED) {

        return ngx_http_next_header_filter(r);

    }

    /* charset: charset index or NGX_HTTP_NO_CHARSET */

    source_charset = ngx_http_source_charset(r, &src);

    if (source_charset == NGX_ERROR) {

        return NGX_ERROR;

    }

    /*

     * source_charset: charset index, NGX_HTTP_NO_CHARSET,

     *                 or NGX_HTTP_CHARSET_OFF

     */

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "charset: \"%V\" > \"%V\"", &src, &dst);

    if (source_charset == NGX_HTTP_CHARSET_OFF) {

        ngx_http_set_charset(r, &dst);

        return ngx_http_next_header_filter(r);

    }

    if (charset == NGX_HTTP_NO_CHARSET

        || source_charset == NGX_HTTP_NO_CHARSET)

    {

        if (source_charset != charset

            || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)

        {

            goto no_charset_map;

        }

        ngx_http_set_charset(r, &dst);

        return ngx_http_next_header_filter(r);

    }

    if (source_charset == charset) {

        r->headers_out.content_type.len = r->headers_out.content_type_len;

        ngx_http_set_charset(r, &dst);

        return ngx_http_next_header_filter(r);

    }

    /* source_charset != charset */

    if (r->headers_out.content_encoding

        && r->headers_out.content_encoding->value.len)

    {

        return ngx_http_next_header_filter(r);

    }

    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);

    charsets = mcf->charsets.elts;

    if (charsets[source_charset].tables == NULL

        || charsets[source_charset].tables[charset] == NULL)

    {

        goto no_charset_map;

    }

    r->headers_out.content_type.len = r->headers_out.content_type_len;

    ngx_http_set_charset(r, &dst);

    return ngx_http_charset_ctx(r, charsets, charset, source_charset);

no_charset_map:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "no \"charset_map\" between the charsets \"%V\" and \"%V\"",

                  &src, &dst);

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)

{

    ngx_int_t                      charset;

    ngx_http_charset_t            *charsets;

    ngx_http_variable_value_t     *vv;

    ngx_http_charset_loc_conf_t   *mlcf;

    ngx_http_charset_main_conf_t  *mcf;

    if (r->headers_out.content_type.len == 0) {

        return NGX_DECLINED;

    }

    if (r->headers_out.override_charset

        && r->headers_out.override_charset->len)

    {

        *name = *r->headers_out.override_charset;

        charset = ngx_http_get_charset(r, name);

        if (charset != NGX_HTTP_NO_CHARSET) {

            return charset;

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "unknown charset \"%V\" to override", name);

        return NGX_DECLINED;

    }

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);

    charset = mlcf->charset;

    if (charset == NGX_HTTP_CHARSET_OFF) {

        return NGX_DECLINED;

    }

    if (r->headers_out.charset.len) {

        if (mlcf->override_charset == 0) {

            return NGX_DECLINED;

        }

    } else {

        if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {

            return NGX_DECLINED;

        }

    }

    if (charset < NGX_HTTP_CHARSET_VAR) {

        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);

        charsets = mcf->charsets.elts;

        *name = charsets[charset].name;

        return charset;

    }

    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);

    if (vv == NULL || vv->not_found) {

        return NGX_ERROR;

    }

    name->len = vv->len;

    name->data = vv->data;

    return ngx_http_get_charset(r, name);

}

static ngx_int_t

‌ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)

{

    ngx_int_t                charset;

    ngx_str_t               *main_charset;

    ngx_http_charset_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);

    if (ctx) {

        *src = ctx->charset_name;

        return ctx->charset;

    }

    main_charset = &r->main->headers_out.charset;

    if (main_charset->len == 0) {

        return NGX_DECLINED;

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);

    charset = ngx_http_get_charset(r, main_charset);

    ctx->charset = charset;

    ctx->charset_name = *main_charset;

    *src = *main_charset;

    return charset;

}

static ngx_int_t

‌ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)

{

    ngx_int_t                      charset;

    ngx_http_charset_t            *charsets;

    ngx_http_variable_value_t     *vv;

    ngx_http_charset_loc_conf_t   *lcf;

    ngx_http_charset_main_conf_t  *mcf;

    if (r->headers_out.charset.len) {

        *name = r->headers_out.charset;

        return ngx_http_get_charset(r, name);

    }

    lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);

    charset = lcf->source_charset;

    if (charset == NGX_HTTP_CHARSET_OFF) {

        name->len = 0;

        return charset;

    }

    if (charset < NGX_HTTP_CHARSET_VAR) {

        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);

        charsets = mcf->charsets.elts;

        *name = charsets[charset].name;

        return charset;

    }

    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);

    if (vv == NULL || vv->not_found) {

        return NGX_ERROR;

    }

    name->len = vv->len;

    name->data = vv->data;

    return ngx_http_get_charset(r, name);

}

static ngx_int_t

‌ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)

{

    ngx_uint_t                     i, n;

    ngx_http_charset_t            *charset;

    ngx_http_charset_main_conf_t  *mcf;

    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);

    charset = mcf->charsets.elts;

    n = mcf->charsets.nelts;

    for (i = 0; i < n; i++) {

        if (charset[i].name.len != name->len) {

            continue;

        }

        if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {

            return i;

        }

    }

    return NGX_HTTP_NO_CHARSET;

}

static ngx_inline void

‌ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)

{

    if (r != r->main) {

        return;

    }

    if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY

        || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)

    {

        /*

         * do not set charset for the redirect because NN 4.x

         * use this charset instead of the next page charset

         */

        r->headers_out.charset.len = 0;

        return;

    }

    r->headers_out.charset = *charset;

}

static ngx_int_t

‌ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,

    ngx_int_t charset, ngx_int_t source_charset)

{

    ngx_http_charset_ctx_t  *ctx;

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);

    ctx->table = charsets[source_charset].tables[charset];

    ctx->charset = charset;

    ctx->charset_name = charsets[charset].name;

    ctx->length = charsets[charset].length;

    ctx->from_utf8 = charsets[source_charset].utf8;

    ctx->to_utf8 = charsets[charset].utf8;

    r->filter_need_in_memory = 1;

    if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {

        ngx_http_clear_content_length(r);

    } else {

        r->filter_need_temporary = 1;

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_int_t                rc;

    ngx_buf_t               *b;

    ngx_chain_t             *cl, *out, **ll;

    ngx_http_charset_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);

    if (ctx == NULL || ctx->table == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {

        out = NULL;

        ll = &out;

        for (cl = in; cl; cl = cl->next) {

            b = cl->buf;

            if (ngx_buf_size(b) == 0) {

                *ll = ngx_alloc_chain_link(r->pool);

                if (*ll == NULL) {

                    return NGX_ERROR;

                }

                (*ll)->buf = b;

                (*ll)->next = NULL;

                ll = &(*ll)->next;

                continue;

            }

            if (ctx->to_utf8) {

                *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);

            } else {

                *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);

            }

            if (*ll == NULL) {

                return NGX_ERROR;

            }

            while (*ll) {

                ll = &(*ll)->next;

            }

        }

        rc = ngx_http_next_body_filter(r, out);

        if (out) {

            if (ctx->busy == NULL) {

                ctx->busy = out;

            } else {

                for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }

                cl->next = out;

            }

        }

        while (ctx->busy) {

            cl = ctx->busy;

            b = cl->buf;

            if (ngx_buf_size(b) != 0) {

                break;

            }

            ctx->busy = cl->next;

            if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {

                continue;

            }

            if (b->shadow) {

                b->shadow->pos = b->shadow->last;

            }

            if (b->pos) {

                cl->next = ctx->free_buffers;

                ctx->free_buffers = cl;

                continue;

            }

            cl->next = ctx->free_bufs;

            ctx->free_bufs = cl;

        }

        return rc;

    }

    for (cl = in; cl; cl = cl->next) {

        (void) ngx_http_charset_recode(cl->buf, ctx->table);

    }

    return ngx_http_next_body_filter(r, in);

}

static ngx_uint_t

‌ngx_http_charset_recode(ngx_buf_t *b, u_char *table)

{

    u_char  *p, *last;

    last = b->last;

    for (p = b->pos; p < last; p++) {

        if (*p != table[*p]) {

            goto recode;

        }

    }

    return 0;

recode:

    do {

        if (*p != table[*p]) {

            *p = table[*p];

        }

        p++;

    } while (p < last);

    b->in_file = 0;

    return 1;

}

static ngx_chain_t *

‌ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,

    ngx_http_charset_ctx_t *ctx)

{

    size_t        len, size;

    u_char        c, *p, *src, *dst, *saved, **table;

    uint32_t      n;

    ngx_buf_t    *b;

    ngx_uint_t    i;

    ngx_chain_t  *out, *cl, **ll;

    src = buf->pos;

    if (ctx->saved_len == 0) {

        for ( /* void */ ; src < buf->last; src++) {

            if (*src < 0x80) {

                continue;

            }

            len = src - buf->pos;

            if (len > 512) {

                out = ngx_http_charset_get_buf(pool, ctx);

                if (out == NULL) {

                    return NULL;

                }

                b = out->buf;

                b->temporary = buf->temporary;

                b->memory = buf->memory;

                b->mmap = buf->mmap;

                b->flush = buf->flush;

                b->pos = buf->pos;

                b->last = src;

                out->buf = b;

                out->next = NULL;

                size = buf->last - src;

                saved = src;

                n = ngx_utf8_decode(&saved, size);

                if (n == 0xfffffffe) {

                    /* incomplete UTF-8 symbol */

                    ngx_memcpy(ctx->saved, src, size);

                    ctx->saved_len = size;

                    b->shadow = buf;

                    return out;

                }

            } else {

                out = NULL;

                size = len + buf->last - src;

                src = buf->pos;

            }

            if (size < NGX_HTML_ENTITY_LEN) {

                size += NGX_HTML_ENTITY_LEN;

            }

            cl = ngx_http_charset_get_buffer(pool, ctx, size);

            if (cl == NULL) {

                return NULL;

            }

            if (out) {

                out->next = cl;

            } else {

                out = cl;

            }

            b = cl->buf;

            dst = b->pos;

            goto recode;

        }

        out = ngx_alloc_chain_link(pool);

        if (out == NULL) {

            return NULL;

        }

        out->buf = buf;

        out->next = NULL;

        return out;

    }

    /* process incomplete UTF sequence from previous buffer */

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,

                   "http charset utf saved: %z", ctx->saved_len);

    p = src;

    for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {

        ctx->saved[i] = *p++;

        if (p == buf->last) {

            break;

        }

    }

    saved = ctx->saved;

    n = ngx_utf8_decode(&saved, i);

    c = '\0';

    if (n < 0x10000) {

        table = (u_char **) ctx->table;

        p = table[n >> 8];

        if (p) {

            c = p[n & 0xff];

        }

    } else if (n == 0xfffffffe) {

        /* incomplete UTF-8 symbol */

        if (i < NGX_UTF_LEN) {

            out = ngx_http_charset_get_buf(pool, ctx);

            if (out == NULL) {

                return NULL;

            }

            b = out->buf;

            b->pos = buf->pos;

            b->last = buf->last;

            b->sync = 1;

            b->shadow = buf;

            ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);

            ctx->saved_len += i;

            return out;

        }

    }

    size = buf->last - buf->pos;

    if (size < NGX_HTML_ENTITY_LEN) {

        size += NGX_HTML_ENTITY_LEN;

    }

    cl = ngx_http_charset_get_buffer(pool, ctx, size);

    if (cl == NULL) {

        return NULL;

    }

    out = cl;

    b = cl->buf;

    dst = b->pos;

    if (c) {

        *dst++ = c;

    } else if (n == 0xfffffffe) {

        *dst++ = '?';

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,

                       "http charset invalid utf 0");

        saved = &ctx->saved[NGX_UTF_LEN];

    } else if (n > 0x10ffff) {

        *dst++ = '?';

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,

                       "http charset invalid utf 1");

    } else {

        dst = ngx_sprintf(dst, "&#%uD;", n);

    }

    src += (saved - ctx->saved) - ctx->saved_len;

    ctx->saved_len = 0;

recode:

    ll = &cl->next;

    table = (u_char **) ctx->table;

    while (src < buf->last) {

        if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {

            b->last = dst;

            size = buf->last - src + NGX_HTML_ENTITY_LEN;

            cl = ngx_http_charset_get_buffer(pool, ctx, size);

            if (cl == NULL) {

                return NULL;

            }

            *ll = cl;

            ll = &cl->next;

            b = cl->buf;

            dst = b->pos;

        }

        if (*src < 0x80) {

            *dst++ = *src++;

            continue;

        }

        len = buf->last - src;

        n = ngx_utf8_decode(&src, len);

        if (n < 0x10000) {

            p = table[n >> 8];

            if (p) {

                c = p[n & 0xff];

                if (c) {

                    *dst++ = c;

                    continue;

                }

            }

            dst = ngx_sprintf(dst, "&#%uD;", n);

            continue;

        }

        if (n == 0xfffffffe) {

            /* incomplete UTF-8 symbol */

            ngx_memcpy(ctx->saved, src, len);

            ctx->saved_len = len;

            if (b->pos == dst) {

                b->sync = 1;

                b->temporary = 0;

            }

            break;

        }

        if (n > 0x10ffff) {

            *dst++ = '?';

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,

                           "http charset invalid utf 2");

            continue;

        }

        /* n > 0xffff */

        dst = ngx_sprintf(dst, "&#%uD;", n);

    }

    b->last = dst;

    b->last_buf = buf->last_buf;

    b->last_in_chain = buf->last_in_chain;

    b->flush = buf->flush;

    b->shadow = buf;

    return out;

}

static ngx_chain_t *

‌ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,

    ngx_http_charset_ctx_t *ctx)

{

    size_t        len, size;

    u_char       *p, *src, *dst, *table;

    ngx_buf_t    *b;

    ngx_chain_t  *out, *cl, **ll;

    table = ctx->table;

    for (src = buf->pos; src < buf->last; src++) {

        if (table[*src * NGX_UTF_LEN] == '\1') {

            continue;

        }

        goto recode;

    }

    out = ngx_alloc_chain_link(pool);

    if (out == NULL) {

        return NULL;

    }

    out->buf = buf;

    out->next = NULL;

    return out;

recode:

    /*

     * we assume that there are about half of characters to be recoded,

     * so we preallocate "size / 2 + size / 2 * ctx->length"

     */

    len = src - buf->pos;

    if (len > 512) {

        out = ngx_http_charset_get_buf(pool, ctx);

        if (out == NULL) {

            return NULL;

        }

        b = out->buf;

        b->temporary = buf->temporary;

        b->memory = buf->memory;

        b->mmap = buf->mmap;

        b->flush = buf->flush;

        b->pos = buf->pos;

        b->last = src;

        out->buf = b;

        out->next = NULL;

        size = buf->last - src;

        size = size / 2 + size / 2 * ctx->length;

    } else {

        out = NULL;

        size = buf->last - src;

        size = len + size / 2 + size / 2 * ctx->length;

        src = buf->pos;

    }

    cl = ngx_http_charset_get_buffer(pool, ctx, size);

    if (cl == NULL) {

        return NULL;

    }

    if (out) {

        out->next = cl;

    } else {

        out = cl;

    }

    ll = &cl->next;

    b = cl->buf;

    dst = b->pos;

    while (src < buf->last) {

        p = &table[*src++ * NGX_UTF_LEN];

        len = *p++;

        if ((size_t) (b->end - dst) < len) {

            b->last = dst;

            size = buf->last - src;

            size = len + size / 2 + size / 2 * ctx->length;

            cl = ngx_http_charset_get_buffer(pool, ctx, size);

            if (cl == NULL) {

                return NULL;

            }

            *ll = cl;

            ll = &cl->next;

            b = cl->buf;

            dst = b->pos;

        }

        while (len) {

            *dst++ = *p++;

            len--;

        }

    }

    b->last = dst;

    b->last_buf = buf->last_buf;

    b->last_in_chain = buf->last_in_chain;

    b->flush = buf->flush;

    b->shadow = buf;

    return out;

}

static ngx_chain_t *

‌ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)

{

    ngx_chain_t  *cl;

    cl = ctx->free_bufs;

    if (cl) {

        ctx->free_bufs = cl->next;

        cl->buf->shadow = NULL;

        cl->next = NULL;

        return cl;

    }

    cl = ngx_alloc_chain_link(pool);

    if (cl == NULL) {

        return NULL;

    }

    cl->buf = ngx_calloc_buf(pool);

    if (cl->buf == NULL) {

        return NULL;

    }

    cl->next = NULL;

    cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;

    return cl;

}

static ngx_chain_t *

‌ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,

    size_t size)

{

    ngx_buf_t    *b;

    ngx_chain_t  *cl, **ll;

    for (ll = &ctx->free_buffers, cl = ctx->free_buffers;

         cl;

         ll = &cl->next, cl = cl->next)

    {

        b = cl->buf;

        if ((size_t) (b->end - b->start) >= size) {

            *ll = cl->next;

            cl->next = NULL;

            b->pos = b->start;

            b->temporary = 1;

            b->shadow = NULL;

            return cl;

        }

    }

    cl = ngx_alloc_chain_link(pool);

    if (cl == NULL) {

        return NULL;

    }

    cl->buf = ngx_create_temp_buf(pool, size);

    if (cl->buf == NULL) {

        return NULL;

    }

    cl->next = NULL;

    cl->buf->temporary = 1;

    cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;

    return cl;

}

static char *

‌ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_charset_main_conf_t  *mcf = conf;

    char                         *rv;

    u_char                       *p, *dst2src, **pp;

    ngx_int_t                     src, dst;

    ngx_uint_t                    i, n;

    ngx_str_t                    *value;

    ngx_conf_t                    pvcf;

    ngx_http_charset_t           *charset;

    ngx_http_charset_tables_t    *table;

    ngx_http_charset_conf_ctx_t   ctx;

    value = cf->args->elts;

    src = ngx_http_add_charset(&mcf->charsets, &value[1]);

    if (src == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    dst = ngx_http_add_charset(&mcf->charsets, &value[2]);

    if (dst == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    if (src == dst) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"charset_map\" between the same charsets "

                           "\"%V\" and \"%V\"", &value[1], &value[2]);

        return NGX_CONF_ERROR;

    }

    table = mcf->tables.elts;

    for (i = 0; i < mcf->tables.nelts; i++) {

        if ((src == table->src && dst == table->dst)

             || (src == table->dst && dst == table->src))

        {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "duplicate \"charset_map\" between "

                               "\"%V\" and \"%V\"", &value[1], &value[2]);

            return NGX_CONF_ERROR;

        }

    }

    table = ngx_array_push(&mcf->tables);

    if (table == NULL) {

        return NGX_CONF_ERROR;

    }

    table->src = src;

    table->dst = dst;

    if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {

        table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);

        if (table->src2dst == NULL) {

            return NGX_CONF_ERROR;

        }

        table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));

        if (table->dst2src == NULL) {

            return NGX_CONF_ERROR;

        }

        dst2src = ngx_pcalloc(cf->pool, 256);

        if (dst2src == NULL) {

            return NGX_CONF_ERROR;

        }

        pp = (u_char **) &table->dst2src[0];

        pp[0] = dst2src;

        for (i = 0; i < 128; i++) {

            p = &table->src2dst[i * NGX_UTF_LEN];

            p[0] = '\1';

            p[1] = (u_char) i;

            dst2src[i] = (u_char) i;

        }

        for (/* void */; i < 256; i++) {

            p = &table->src2dst[i * NGX_UTF_LEN];

            p[0] = '\1';

            p[1] = '?';

        }

    } else {

        table->src2dst = ngx_palloc(cf->pool, 256);

        if (table->src2dst == NULL) {

            return NGX_CONF_ERROR;

        }

        table->dst2src = ngx_palloc(cf->pool, 256);

        if (table->dst2src == NULL) {

            return NGX_CONF_ERROR;

        }

        for (i = 0; i < 128; i++) {

            table->src2dst[i] = (u_char) i;

            table->dst2src[i] = (u_char) i;

        }

        for (/* void */; i < 256; i++) {

            table->src2dst[i] = '?';

            table->dst2src[i] = '?';

        }

    }

    charset = mcf->charsets.elts;

    ctx.table = table;

    ctx.charset = &charset[dst];

    ctx.characters = 0;

    pvcf = *cf;

    cf->ctx = &ctx;

    cf->handler = ngx_http_charset_map;

    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pvcf;

    if (ctx.characters) {

        n = ctx.charset->length;

        ctx.charset->length /= ctx.characters;

        if (((n * 10) / ctx.characters) % 10 > 4) {

            ctx.charset->length++;

        }

    }

    return rv;

}

static char *

‌ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)

{

    u_char                       *p, *dst2src, **pp;

    uint32_t                      n;

    ngx_int_t                     src, dst;

    ngx_str_t                    *value;

    ngx_uint_t                    i;

    ngx_http_charset_tables_t    *table;

    ngx_http_charset_conf_ctx_t  *ctx;

    if (cf->args->nelts != 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    src = ngx_hextoi(value[0].data, value[0].len);

    if (src == NGX_ERROR || src > 255) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid value \"%V\"", &value[0]);

        return NGX_CONF_ERROR;

    }

    ctx = cf->ctx;

    table = ctx->table;

    if (ctx->charset->utf8) {

        p = &table->src2dst[src * NGX_UTF_LEN];

        *p++ = (u_char) (value[1].len / 2);

        for (i = 0; i < value[1].len; i += 2) {

            dst = ngx_hextoi(&value[1].data[i], 2);

            if (dst == NGX_ERROR || dst > 255) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid value \"%V\"", &value[1]);

                return NGX_CONF_ERROR;

            }

            *p++ = (u_char) dst;

        }

        i /= 2;

        ctx->charset->length += i;

        ctx->characters++;

        p = &table->src2dst[src * NGX_UTF_LEN] + 1;

        n = ngx_utf8_decode(&p, i);

        if (n > 0xffff) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        pp = (u_char **) &table->dst2src[0];

        dst2src = pp[n >> 8];

        if (dst2src == NULL) {

            dst2src = ngx_pcalloc(cf->pool, 256);

            if (dst2src == NULL) {

                return NGX_CONF_ERROR;

            }

            pp[n >> 8] = dst2src;

        }

        dst2src[n & 0xff] = (u_char) src;

    } else {

        dst = ngx_hextoi(value[1].data, value[1].len);

        if (dst == NGX_ERROR || dst > 255) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        table->src2dst[src] = (u_char) dst;

        table->dst2src[dst] = (u_char) src;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char  *p = conf;

    ngx_int_t                     *cp;

    ngx_str_t                     *value, var;

    ngx_http_charset_main_conf_t  *mcf;

    cp = (ngx_int_t *) (p + cmd->offset);

    if (*cp != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)

        && ngx_strcmp(value[1].data, "off") == 0)

    {

        *cp = NGX_HTTP_CHARSET_OFF;

        return NGX_CONF_OK;

    }

    if (value[1].data[0] == '$') {

        var.len = value[1].len - 1;

        var.data = value[1].data + 1;

        *cp = ngx_http_get_variable_index(cf, &var);

        if (*cp == NGX_ERROR) {

            return NGX_CONF_ERROR;

        }

        *cp += NGX_HTTP_CHARSET_VAR;

        return NGX_CONF_OK;

    }

    mcf = ngx_http_conf_get_module_main_conf(cf,

                                             ngx_http_charset_filter_module);

    *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);

    if (*cp == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)

{

    ngx_uint_t           i;

    ngx_http_charset_t  *c;

    c = charsets->elts;

    for (i = 0; i < charsets->nelts; i++) {

        if (name->len != c[i].name.len) {

            continue;

        }

        if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {

            break;

        }

    }

    if (i < charsets->nelts) {

        return i;

    }

    c = ngx_array_push(charsets);

    if (c == NULL) {

        return NGX_ERROR;

    }

    c->tables = NULL;

    c->name = *name;

    c->length = 0;

    if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {

        c->utf8 = 1;

    } else {

        c->utf8 = 0;

    }

    return i;

}

static void *

‌ngx_http_charset_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_charset_main_conf_t  *mcf;

    mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));

    if (mcf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))

        != NGX_OK)

    {

        return NULL;

    }

    if (ngx_array_init(&mcf->tables, cf->pool, 1,

                       sizeof(ngx_http_charset_tables_t))

        != NGX_OK)

    {

        return NULL;

    }

    if (ngx_array_init(&mcf->recodes, cf->pool, 2,

                       sizeof(ngx_http_charset_recode_t))

        != NGX_OK)

    {

        return NULL;

    }

    return mcf;

}

static void *

‌ngx_http_charset_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_charset_loc_conf_t  *lcf;

    lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));

    if (lcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     lcf->types = { NULL };

     *     lcf->types_keys = NULL;

     */

    lcf->charset = NGX_CONF_UNSET;

    lcf->source_charset = NGX_CONF_UNSET;

    lcf->override_charset = NGX_CONF_UNSET;

    return lcf;

}

static char *

‌ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_charset_loc_conf_t *prev = parent;

    ngx_http_charset_loc_conf_t *conf = child;

    ngx_uint_t                     i;

    ngx_http_charset_recode_t     *recode;

    ngx_http_charset_main_conf_t  *mcf;

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_charset_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);

    ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);

    ngx_conf_merge_value(conf->source_charset, prev->source_charset,

                         NGX_HTTP_CHARSET_OFF);

    if (conf->charset == NGX_HTTP_CHARSET_OFF

        || conf->source_charset == NGX_HTTP_CHARSET_OFF

        || conf->charset == conf->source_charset)

    {

        return NGX_CONF_OK;

    }

    if (conf->source_charset >= NGX_HTTP_CHARSET_VAR

        || conf->charset >= NGX_HTTP_CHARSET_VAR)

    {

        return NGX_CONF_OK;

    }

    mcf = ngx_http_conf_get_module_main_conf(cf,

                                             ngx_http_charset_filter_module);

    recode = mcf->recodes.elts;

    for (i = 0; i < mcf->recodes.nelts; i++) {

        if (conf->source_charset == recode[i].src

            && conf->charset == recode[i].dst)

        {

            return NGX_CONF_OK;

        }

    }

    recode = ngx_array_push(&mcf->recodes);

    if (recode == NULL) {

        return NGX_CONF_ERROR;

    }

    recode->src = conf->source_charset;

    recode->dst = conf->charset;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_charset_postconfiguration(ngx_conf_t *cf)

{

    u_char                       **src, **dst;

    ngx_int_t                      c;

    ngx_uint_t                     i, t;

    ngx_http_charset_t            *charset;

    ngx_http_charset_recode_t     *recode;

    ngx_http_charset_tables_t     *tables;

    ngx_http_charset_main_conf_t  *mcf;

    mcf = ngx_http_conf_get_module_main_conf(cf,

                                             ngx_http_charset_filter_module);

    recode = mcf->recodes.elts;

    tables = mcf->tables.elts;

    charset = mcf->charsets.elts;

    for (i = 0; i < mcf->recodes.nelts; i++) {

        c = recode[i].src;

        for (t = 0; t < mcf->tables.nelts; t++) {

            if (c == tables[t].src && recode[i].dst == tables[t].dst) {

                goto next;

            }

            if (c == tables[t].dst && recode[i].dst == tables[t].src) {

                goto next;

            }

        }

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",

                   &charset[c].name, &charset[recode[i].dst].name);

        return NGX_ERROR;

    next:

        continue;

    }

    for (t = 0; t < mcf->tables.nelts; t++) {

        src = charset[tables[t].src].tables;

        if (src == NULL) {

            src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);

            if (src == NULL) {

                return NGX_ERROR;

            }

            charset[tables[t].src].tables = src;

        }

        dst = charset[tables[t].dst].tables;

        if (dst == NULL) {

            dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);

            if (dst == NULL) {

                return NGX_ERROR;

            }

            charset[tables[t].dst].tables = dst;

        }

        src[tables[t].dst] = tables[t].src2dst;

        dst[tables[t].src] = tables[t].dst2src;

    }

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_charset_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_charset_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_chunked_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_chunked_filter_module

ngx_http_chunked_filter_module_ctx

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

ngx_http_chunked_filter_ctx_t

Functions defined

ngx_http_chunked_body_filter

ngx_http_chunked_filter_init

ngx_http_chunked_header_filter

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_chain_t         *free;

    ngx_chain_t         *busy;

‌} ngx_http_chunked_filter_ctx_t;

static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_chunked_filter_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 */

};

‌ngx_module_t  ngx_http_chunked_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_chunked_filter_module_ctx,   /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_chunked_header_filter(ngx_http_request_t *r)

{

    ngx_http_core_loc_conf_t       *clcf;

    ngx_http_chunked_filter_ctx_t  *ctx;

    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED

        || r->headers_out.status == NGX_HTTP_NO_CONTENT

        || r->headers_out.status < NGX_HTTP_OK

        || r != r->main

        || (r->method & NGX_HTTP_HEAD))

    {

        return ngx_http_next_header_filter(r);

    }

    if (r->headers_out.content_length_n == -1) {

        if (r->http_version < NGX_HTTP_VERSION_11) {

            r->keepalive = 0;

        } else {

            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

            if (clcf->chunked_transfer_encoding) {

                r->chunked = 1;

                ctx = ngx_pcalloc(r->pool,

                                  sizeof(ngx_http_chunked_filter_ctx_t));

                if (ctx == NULL) {

                    return NGX_ERROR;

                }

                ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);

            } else {

                r->keepalive = 0;

            }

        }

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    u_char                         *chunk;

    off_t                           size;

    ngx_int_t                       rc;

    ngx_buf_t                      *b;

    ngx_chain_t                    *out, *cl, *tl, **ll;

    ngx_http_chunked_filter_ctx_t  *ctx;

    if (in == NULL || !r->chunked || r->header_only) {

        return ngx_http_next_body_filter(r, in);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);

    out = NULL;

    ll = &out;

    size = 0;

    cl = in;

    for ( ;; ) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http chunk: %d", ngx_buf_size(cl->buf));

        size += ngx_buf_size(cl->buf);

        if (cl->buf->flush

            || cl->buf->sync

            || ngx_buf_in_memory(cl->buf)

            || cl->buf->in_file)

        {

            tl = ngx_alloc_chain_link(r->pool);

            if (tl == NULL) {

                return NGX_ERROR;

            }

            tl->buf = cl->buf;

            *ll = tl;

            ll = &tl->next;

        }

        if (cl->next == NULL) {

            break;

        }

        cl = cl->next;

    }

    if (size) {

        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);

        if (tl == NULL) {

            return NGX_ERROR;

        }

        b = tl->buf;

        chunk = b->start;

        if (chunk == NULL) {

            /* the "0000000000000000" is 64-bit hexadecimal string */

            chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);

            if (chunk == NULL) {

                return NGX_ERROR;

            }

            b->start = chunk;

            b->end = chunk + sizeof("0000000000000000" CRLF) - 1;

        }

        b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;

        b->memory = 0;

        b->temporary = 1;

        b->pos = chunk;

        b->last = ngx_sprintf(chunk, "%xO" CRLF, size);

        tl->next = out;

        out = tl;

    }

    if (cl->buf->last_buf) {

        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);

        if (tl == NULL) {

            return NGX_ERROR;

        }

        b = tl->buf;

        b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;

        b->temporary = 0;

        b->memory = 1;

        b->last_buf = 1;

        b->pos = (u_char *) CRLF "0" CRLF CRLF;

        b->last = b->pos + 7;

        cl->buf->last_buf = 0;

        *ll = tl;

        if (size == 0) {

            b->pos += 2;

        }

    } else if (size > 0) {

        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);

        if (tl == NULL) {

            return NGX_ERROR;

        }

        b = tl->buf;

        b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;

        b->temporary = 0;

        b->memory = 1;

        b->pos = (u_char *) CRLF;

        b->last = b->pos + 2;

        *ll = tl;

    } else {

        *ll = NULL;

    }

    rc = ngx_http_next_body_filter(r, out);

    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,

                            (ngx_buf_tag_t) &ngx_http_chunked_filter_module);

    return rc;

}

static ngx_int_t

‌ngx_http_chunked_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_chunked_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_chunked_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_dav_module.c - nginx-1.7.9

Global variables defined

ngx_http_dav_commands

ngx_http_dav_methods_mask

ngx_http_dav_module

ngx_http_dav_module_ctx

Data types defined

ngx_http_dav_copy_ctx_t

ngx_http_dav_loc_conf_t

Functions defined

ngx_http_dav_copy_dir

ngx_http_dav_copy_dir_time

ngx_http_dav_copy_move_handler

ngx_http_dav_copy_tree_file

ngx_http_dav_create_loc_conf

ngx_http_dav_delete_dir

ngx_http_dav_delete_file

ngx_http_dav_delete_handler

ngx_http_dav_delete_path

ngx_http_dav_depth

ngx_http_dav_error

ngx_http_dav_handler

ngx_http_dav_init

ngx_http_dav_location

ngx_http_dav_merge_loc_conf

ngx_http_dav_mkcol_handler

ngx_http_dav_noop

ngx_http_dav_put_handler

Macros defined

NGX_HTTP_DAV_INFINITY_DEPTH

NGX_HTTP_DAV_INVALID_DEPTH

NGX_HTTP_DAV_NO_DEPTH

NGX_HTTP_DAV_OFF

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_DAV_OFF             2

‌#define NGX_HTTP_DAV_NO_DEPTH        -3

‌#define NGX_HTTP_DAV_INVALID_DEPTH   -2

‌#define NGX_HTTP_DAV_INFINITY_DEPTH  -1

typedef struct {

    ngx_uint_t  methods;

    ngx_uint_t  access;

    ngx_uint_t  min_delete_depth;

    ngx_flag_t  create_full_put_path;

‌} ngx_http_dav_loc_conf_t;

typedef struct {

    ngx_str_t   path;

    size_t      len;

‌} ngx_http_dav_copy_ctx_t;

static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);

static void ngx_http_dav_put_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,

    ngx_str_t *path, ngx_uint_t dir);

static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);

static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);

static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);

static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,

    ngx_http_dav_loc_conf_t *dlcf);

static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);

static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,

    ngx_str_t *path);

static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);

static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,

    ngx_int_t not_found, char *failed, u_char *path);

static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);

static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);

‌static ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {

    { ngx_string("off"), NGX_HTTP_DAV_OFF },

    { ngx_string("put"), NGX_HTTP_PUT },

    { ngx_string("delete"), NGX_HTTP_DELETE },

    { ngx_string("mkcol"), NGX_HTTP_MKCOL },

    { ngx_string("copy"), NGX_HTTP_COPY },

    { ngx_string("move"), NGX_HTTP_MOVE },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_http_dav_commands[] = {

    { ngx_string("dav_methods"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_dav_loc_conf_t, methods),

      &ngx_http_dav_methods_mask },

    { ngx_string("create_full_put_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),

      NULL },

    { ngx_string("min_delete_depth"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),

      NULL },

    { ngx_string("dav_access"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_conf_set_access_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_dav_loc_conf_t, access),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_dav_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_dav_init,                     /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_dav_create_loc_conf,          /* create location configuration */

    ngx_http_dav_merge_loc_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_dav_module = {

    NGX_MODULE_V1,

    &ngx_http_dav_module_ctx,              /* module context */

    ngx_http_dav_commands,                 /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_dav_handler(ngx_http_request_t *r)

{

    ngx_int_t                 rc;

    ngx_http_dav_loc_conf_t  *dlcf;

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    if (!(r->method & dlcf->methods)) {

        return NGX_DECLINED;

    }

    switch (r->method) {

    case NGX_HTTP_PUT:

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

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "cannot PUT to a collection");

            return NGX_HTTP_CONFLICT;

        }

        r->request_body_in_file_only = 1;

        r->request_body_in_persistent_file = 1;

        r->request_body_in_clean_file = 1;

        r->request_body_file_group_access = 1;

        r->request_body_file_log_level = 0;

        rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);

        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

            return rc;

        }

        return NGX_DONE;

    case NGX_HTTP_DELETE:

        return ngx_http_dav_delete_handler(r);

    case NGX_HTTP_MKCOL:

        return ngx_http_dav_mkcol_handler(r, dlcf);

    case NGX_HTTP_COPY:

        return ngx_http_dav_copy_move_handler(r);

    case NGX_HTTP_MOVE:

        return ngx_http_dav_copy_move_handler(r);

    }

    return NGX_DECLINED;

}

static void

‌ngx_http_dav_put_handler(ngx_http_request_t *r)

{

    size_t                    root;

    time_t                    date;

    ngx_str_t                *temp, path;

    ngx_uint_t                status;

    ngx_file_info_t           fi;

    ngx_ext_rename_file_t     ext;

    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->request_body == NULL || r->request_body->temp_file == NULL) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    path.len--;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http put filename: \"%s\"", path.data);

    temp = &r->request_body->temp_file->file.name;

    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {

        status = NGX_HTTP_CREATED;

    } else {

        status = NGX_HTTP_NO_CONTENT;

        if (ngx_is_dir(&fi)) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,

                          "\"%s\" could not be created", path.data);

            if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                              ngx_delete_file_n " \"%s\" failed",

                              temp->data);

            }

            ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);

            return;

        }

    }

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    ext.access = dlcf->access;

    ext.path_access = dlcf->access;

    ext.time = -1;

    ext.create_path = dlcf->create_full_put_path;

    ext.delete_file = 1;

    ext.log = r->connection->log;

    if (r->headers_in.date) {

        date = ngx_http_parse_time(r->headers_in.date->value.data,

                                   r->headers_in.date->value.len);

        if (date != NGX_ERROR) {

            ext.time = date;

            ext.fd = r->request_body->temp_file->file.fd;

        }

    }

    if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {

        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

        return;

    }

    if (status == NGX_HTTP_CREATED) {

        if (ngx_http_dav_location(r, path.data) != NGX_OK) {

            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

            return;

        }

        r->headers_out.content_length_n = 0;

    }

    r->headers_out.status = status;

    r->header_only = 1;

    ngx_http_finalize_request(r, ngx_http_send_header(r));

    return;

}

static ngx_int_t

‌ngx_http_dav_delete_handler(ngx_http_request_t *r)

{

    size_t                    root;

    ngx_err_t                 err;

    ngx_int_t                 rc, depth;

    ngx_uint_t                i, d, dir;

    ngx_str_t                 path;

    ngx_file_info_t           fi;

    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->headers_in.content_length_n > 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "DELETE with body is unsupported");

        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;

    }

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    if (dlcf->min_delete_depth) {

        d = 0;

        for (i = 0; i < r->uri.len; /* void */) {

            if (r->uri.data[i++] == '/') {

                if (++d >= dlcf->min_delete_depth && i < r->uri.len) {

                    goto ok;

                }

            }

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "insufficient URI depth:%i to DELETE", d);

        return NGX_HTTP_CONFLICT;

    }

ok:

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http delete filename: \"%s\"", path.data);

    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {

        err = ngx_errno;

        rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;

        return ngx_http_dav_error(r->connection->log, err,

                                  rc, ngx_link_info_n, path.data);

    }

    if (ngx_is_dir(&fi)) {

        if (r->uri.data[r->uri.len - 1] != '/') {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,

                          "DELETE \"%s\" failed", path.data);

            return NGX_HTTP_CONFLICT;

        }

        depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

        if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"Depth\" header must be infinity");

            return NGX_HTTP_BAD_REQUEST;

        }

        path.len -= 2;  /* omit "/\0" */

        dir = 1;

    } else {

        /*

         * we do not need to test (r->uri.data[r->uri.len - 1] == '/')

         * because ngx_link_info("/file/") returned NGX_ENOTDIR above

         */

        depth = ngx_http_dav_depth(r, 0);

        if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"Depth\" header must be 0 or infinity");

            return NGX_HTTP_BAD_REQUEST;

        }

        dir = 0;

    }

    rc = ngx_http_dav_delete_path(r, &path, dir);

    if (rc == NGX_OK) {

        return NGX_HTTP_NO_CONTENT;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)

{

    char            *failed;

    ngx_tree_ctx_t   tree;

    if (dir) {

        tree.init_handler = NULL;

        tree.file_handler = ngx_http_dav_delete_file;

        tree.pre_tree_handler = ngx_http_dav_noop;

        tree.post_tree_handler = ngx_http_dav_delete_dir;

        tree.spec_handler = ngx_http_dav_delete_file;

        tree.data = NULL;

        tree.alloc = 0;

        tree.log = r->connection->log;

        /* TODO: 207 */

        if (ngx_walk_tree(&tree, path) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {

            return NGX_OK;

        }

        failed = ngx_delete_dir_n;

    } else {

        if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {

            return NGX_OK;

        }

        failed = ngx_delete_file_n;

    }

    return ngx_http_dav_error(r->connection->log, ngx_errno,

                              NGX_HTTP_NOT_FOUND, failed, path->data);

}

static ngx_int_t

‌ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http delete dir: \"%s\"", path->data);

    if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {

        /* TODO: add to 207 */

        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,

                                  path->data);

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http delete file: \"%s\"", path->data);

    if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {

        /* TODO: add to 207 */

        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,

                                  path->data);

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)

{

    u_char    *p;

    size_t     root;

    ngx_str_t  path;

    if (r->headers_in.content_length_n > 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "MKCOL with body is unsupported");

        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;

    }

    if (r->uri.data[r->uri.len - 1] != '/') {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "MKCOL can create a collection only");

        return NGX_HTTP_CONFLICT;

    }

    p = ngx_http_map_uri_to_path(r, &path, &root, 0);

    if (p == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    *(p - 1) = '\0';

    r->uri.len--;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http mkcol path: \"%s\"", path.data);

    if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))

        != NGX_FILE_ERROR)

    {

        if (ngx_http_dav_location(r, path.data) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        return NGX_HTTP_CREATED;

    }

    return ngx_http_dav_error(r->connection->log, ngx_errno,

                              NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);

}

static ngx_int_t

‌ngx_http_dav_copy_move_handler(ngx_http_request_t *r)

{

    u_char                   *p, *host, *last, ch;

    size_t                    len, root;

    ngx_err_t                 err;

    ngx_int_t                 rc, depth;

    ngx_uint_t                overwrite, slash, dir, flags;

    ngx_str_t                 path, uri, duri, args;

    ngx_tree_ctx_t            tree;

    ngx_copy_file_t           cf;

    ngx_file_info_t           fi;

    ngx_table_elt_t          *dest, *over;

    ngx_ext_rename_file_t     ext;

    ngx_http_dav_copy_ctx_t   copy;

    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->headers_in.content_length_n > 0) {

        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;

    }

    dest = r->headers_in.destination;

    if (dest == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client sent no \"Destination\" header");

        return NGX_HTTP_BAD_REQUEST;

    }

    p = dest->value.data;

    /* there is always '\0' even after empty header value */

    if (p[0] == '/') {

        last = p + dest->value.len;

        goto destination_done;

    }

    len = r->headers_in.server.len;

    if (len == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client sent no \"Host\" header");

        return NGX_HTTP_BAD_REQUEST;

    }

#if (NGX_HTTP_SSL)

    if (r->connection->ssl) {

        if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)

            != 0)

        {

            goto invalid_destination;

        }

        host = dest->value.data + sizeof("https://") - 1;

    } else

#endif

    {

        if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)

            != 0)

        {

            goto invalid_destination;

        }

        host = dest->value.data + sizeof("http://") - 1;

    }

    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "\"Destination\" URI \"%V\" is handled by "

                      "different repository than the source URI",

                      &dest->value);

        return NGX_HTTP_BAD_REQUEST;

    }

    last = dest->value.data + dest->value.len;

    for (p = host + len; p < last; p++) {

        if (*p == '/') {

            goto destination_done;

        }

    }

invalid_destination:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "client sent invalid \"Destination\" header: \"%V\"",

                  &dest->value);

    return NGX_HTTP_BAD_REQUEST;

destination_done:

    duri.len = last - p;

    duri.data = p;

    flags = NGX_HTTP_LOG_UNSAFE;

    if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {

        goto invalid_destination;

    }

    if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')

        || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))

    {

         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                       "both URI \"%V\" and \"Destination\" URI \"%V\" "

                       "should be either collections or non-collections",

                       &r->uri, &dest->value);

         return NGX_HTTP_CONFLICT;

    }

    depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

    if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

        if (r->method == NGX_HTTP_COPY) {

            if (depth != 0) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "\"Depth\" header must be 0 or infinity");

                return NGX_HTTP_BAD_REQUEST;

            }

        } else {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"Depth\" header must be infinity");

            return NGX_HTTP_BAD_REQUEST;

        }

    }

    over = r->headers_in.overwrite;

    if (over) {

        if (over->value.len == 1) {

            ch = over->value.data[0];

            if (ch == 'T' || ch == 't') {

                overwrite = 1;

                goto overwrite_done;

            }

            if (ch == 'F' || ch == 'f') {

                overwrite = 0;

                goto overwrite_done;

            }

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "client sent invalid \"Overwrite\" header: \"%V\"",

                      &over->value);

        return NGX_HTTP_BAD_REQUEST;

    }

    overwrite = 1;

overwrite_done:

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http copy from: \"%s\"", path.data);

    uri = r->uri;

    r->uri = duri;

    if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    r->uri = uri;

    copy.path.len--;  /* omit "\0" */

    if (copy.path.data[copy.path.len - 1] == '/') {

        slash = 1;

        copy.path.len--;

        copy.path.data[copy.path.len] = '\0';

    } else {

        slash = 0;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http copy to: \"%s\"", copy.path.data);

    if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {

        err = ngx_errno;

        if (err != NGX_ENOENT) {

            return ngx_http_dav_error(r->connection->log, err,

                                      NGX_HTTP_NOT_FOUND, ngx_link_info_n,

                                      copy.path.data);

        }

        /* destination does not exist */

        overwrite = 0;

        dir = 0;

    } else {

        /* destination exists */

        if (ngx_is_dir(&fi) && !slash) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"%V\" could not be %Ved to collection \"%V\"",

                          &r->uri, &r->method_name, &dest->value);

            return NGX_HTTP_CONFLICT;

        }

        if (!overwrite) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,

                          "\"%s\" could not be created", copy.path.data);

            return NGX_HTTP_PRECONDITION_FAILED;

        }

        dir = ngx_is_dir(&fi);

    }

    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {

        return ngx_http_dav_error(r->connection->log, ngx_errno,

                                  NGX_HTTP_NOT_FOUND, ngx_link_info_n,

                                  path.data);

    }

    if (ngx_is_dir(&fi)) {

        if (r->uri.data[r->uri.len - 1] != '/') {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "\"%V\" is collection", &r->uri);

            return NGX_HTTP_BAD_REQUEST;

        }

        if (overwrite) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http delete: \"%s\"", copy.path.data);

            rc = ngx_http_dav_delete_path(r, &copy.path, dir);

            if (rc != NGX_OK) {

                return rc;

            }

        }

    }

    if (ngx_is_dir(&fi)) {

        path.len -= 2;  /* omit "/\0" */

        if (r->method == NGX_HTTP_MOVE) {

            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {

                return NGX_HTTP_CREATED;

            }

        }

        if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))

            == NGX_FILE_ERROR)

        {

            return ngx_http_dav_error(r->connection->log, ngx_errno,

                                      NGX_HTTP_NOT_FOUND,

                                      ngx_create_dir_n, copy.path.data);

        }

        copy.len = path.len;

        tree.init_handler = NULL;

        tree.file_handler = ngx_http_dav_copy_tree_file;

        tree.pre_tree_handler = ngx_http_dav_copy_dir;

        tree.post_tree_handler = ngx_http_dav_copy_dir_time;

        tree.spec_handler = ngx_http_dav_noop;

        tree.data = &copy;

        tree.alloc = 0;

        tree.log = r->connection->log;

        if (ngx_walk_tree(&tree, &path) == NGX_OK) {

            if (r->method == NGX_HTTP_MOVE) {

                rc = ngx_http_dav_delete_path(r, &path, 1);

                if (rc != NGX_OK) {

                    return rc;

                }

            }

            return NGX_HTTP_CREATED;

        }

    } else {

        if (r->method == NGX_HTTP_MOVE) {

            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

            ext.access = 0;

            ext.path_access = dlcf->access;

            ext.time = -1;

            ext.create_path = 1;

            ext.delete_file = 0;

            ext.log = r->connection->log;

            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {

                return NGX_HTTP_NO_CONTENT;

            }

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

        cf.size = ngx_file_size(&fi);

        cf.buf_size = 0;

        cf.access = dlcf->access;

        cf.time = ngx_file_mtime(&fi);

        cf.log = r->connection->log;

        if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {

            return NGX_HTTP_NO_CONTENT;

        }

    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;

}

static ngx_int_t

‌ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    u_char                   *p, *dir;

    size_t                    len;

    ngx_http_dav_copy_ctx_t  *copy;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy dir: \"%s\"", path->data);

    copy = ctx->data;

    len = copy->path.len + path->len;

    dir = ngx_alloc(len + 1, ctx->log);

    if (dir == NULL) {

        return NGX_ABORT;

    }

    p = ngx_cpymem(dir, copy->path.data, copy->path.len);

    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy dir to: \"%s\"", dir);

    if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {

        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,

                                  dir);

    }

    ngx_free(dir);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    u_char                   *p, *dir;

    size_t                    len;

    ngx_http_dav_copy_ctx_t  *copy;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy dir time: \"%s\"", path->data);

    copy = ctx->data;

    len = copy->path.len + path->len;

    dir = ngx_alloc(len + 1, ctx->log);

    if (dir == NULL) {

        return NGX_ABORT;

    }

    p = ngx_cpymem(dir, copy->path.data, copy->path.len);

    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy dir time to: \"%s\"", dir);

#if (NGX_WIN32)

    {

    ngx_fd_t  fd;

    fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

    if (fd == NGX_INVALID_FILE) {

        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);

        goto failed;

    }

    if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {

        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,

                      ngx_set_file_time_n " \"%s\" failed", dir);

    }

    if (ngx_close_file(fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", dir);

    }

    }

failed:

#else

    if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {

        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,

                      ngx_set_file_time_n " \"%s\" failed", dir);

    }

#endif

    ngx_free(dir);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)

{

    u_char                   *p, *file;

    size_t                    len;

    ngx_copy_file_t           cf;

    ngx_http_dav_copy_ctx_t  *copy;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy file: \"%s\"", path->data);

    copy = ctx->data;

    len = copy->path.len + path->len;

    file = ngx_alloc(len + 1, ctx->log);

    if (file == NULL) {

        return NGX_ABORT;

    }

    p = ngx_cpymem(file, copy->path.data, copy->path.len);

    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,

                   "http copy file to: \"%s\"", file);

    cf.size = ctx->size;

    cf.buf_size = 0;

    cf.access = ctx->access;

    cf.time = ctx->mtime;

    cf.log = ctx->log;

    (void) ngx_copy_file(path->data, file, &cf);

    ngx_free(file);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)

{

    ngx_table_elt_t  *depth;

    depth = r->headers_in.depth;

    if (depth == NULL) {

        return dflt;

    }

    if (depth->value.len == 1) {

        if (depth->value.data[0] == '0') {

            return 0;

        }

        if (depth->value.data[0] == '1') {

            return 1;

        }

    } else {

        if (depth->value.len == sizeof("infinity") - 1

            && ngx_strcmp(depth->value.data, "infinity") == 0)

        {

            return NGX_HTTP_DAV_INFINITY_DEPTH;

        }

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "client sent invalid \"Depth\" header: \"%V\"",

                  &depth->value);

    return NGX_HTTP_DAV_INVALID_DEPTH;

}

static ngx_int_t

‌ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,

    char *failed, u_char *path)

{

    ngx_int_t   rc;

    ngx_uint_t  level;

    if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {

        level = NGX_LOG_ERR;

        rc = not_found;

    } else if (err == NGX_EACCES || err == NGX_EPERM) {

        level = NGX_LOG_ERR;

        rc = NGX_HTTP_FORBIDDEN;

    } else if (err == NGX_EEXIST) {

        level = NGX_LOG_ERR;

        rc = NGX_HTTP_NOT_ALLOWED;

    } else if (err == NGX_ENOSPC) {

        level = NGX_LOG_CRIT;

        rc = NGX_HTTP_INSUFFICIENT_STORAGE;

    } else {

        level = NGX_LOG_CRIT;

        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);

    return rc;

}

static ngx_int_t

‌ngx_http_dav_location(ngx_http_request_t *r, u_char *path)

{

    u_char                    *location;

    ngx_http_core_loc_conf_t  *clcf;

    r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));

    if (r->headers_out.location == NULL) {

        return NGX_ERROR;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->alias && clcf->root_lengths == NULL) {

        location = path + clcf->root.len;

    } else {

        location = ngx_pnalloc(r->pool, r->uri.len);

        if (location == NULL) {

            return NGX_ERROR;

        }

        ngx_memcpy(location, r->uri.data, r->uri.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 = r->uri.len;

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

    return NGX_OK;

}

static void *

‌ngx_http_dav_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_dav_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->methods = 0;

     */

    conf->min_delete_depth = NGX_CONF_UNSET_UINT;

    conf->access = NGX_CONF_UNSET_UINT;

    conf->create_full_put_path = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_dav_loc_conf_t  *prev = parent;

    ngx_http_dav_loc_conf_t  *conf = child;

    ngx_conf_merge_bitmask_value(conf->methods, prev->methods,

                         (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));

    ngx_conf_merge_uint_value(conf->min_delete_depth,

                         prev->min_delete_depth, 0);

    ngx_conf_merge_uint_value(conf->access, prev->access, 0600);

    ngx_conf_merge_value(conf->create_full_put_path,

                         prev->create_full_put_path, 0);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_dav_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_dav_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_empty_gif_module.c - nginx-1.7.9

Global variables defined

ngx_empty_gif

ngx_http_empty_gif_commands

ngx_http_empty_gif_module

ngx_http_empty_gif_module_ctx

ngx_http_gif_type

Functions defined

ngx_http_empty_gif

ngx_http_empty_gif_handler

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static char *ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_empty_gif_commands[] = {

    { ngx_string("empty_gif"),

      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

      ngx_http_empty_gif,

      0,

      0,

      NULL },

      ngx_null_command

};

/* the minimal single pixel transparent GIF, 43 bytes */

‌static u_char  ngx_empty_gif[] = {

    'G', 'I', 'F', '8', '9', 'a',  /* header                                 */

                                   /* logical screen descriptor              */

    0x01, 0x00,                    /* logical screen width                   */

    0x01, 0x00,                    /* logical screen height                  */

    0x80,                          /* global 1-bit color table               */

    0x01,                          /* background color #1                    */

    0x00,                          /* no aspect ratio                        */

                                   /* global color table                     */

    0x00, 0x00, 0x00,              /* #0: black                              */

    0xff, 0xff, 0xff,              /* #1: white                              */

                                   /* graphic control extension              */

    0x21,                          /* extension introducer                   */

    0xf9,                          /* graphic control label                  */

    0x04,                          /* block size                             */

    0x01,                          /* transparent color is given,            */

                                   /*     no disposal specified,             */

                                   /*     user input is not expected         */

    0x00, 0x00,                    /* delay time                             */

    0x01,                          /* transparent color #1                   */

    0x00,                          /* block terminator                       */

                                   /* image descriptor                       */

    0x2c,                          /* image separator                        */

    0x00, 0x00,                    /* image left position                    */

    0x00, 0x00,                    /* image top position                     */

    0x01, 0x00,                    /* image width                            */

    0x01, 0x00,                    /* image height                           */

    0x00,                          /* no local color table, no interlaced    */

                                   /* table based image data                 */

    0x02,                          /* LZW minimum code size,                 */

                                   /*     must be at least 2-bit             */

    0x02,                          /* block size                             */

    0x4c, 0x01,                    /* compressed bytes 01_001_100, 0000000_1 */

                                   /* 100: clear code                        */

                                   /* 001: 1                                 */

                                   /* 101: end of information code           */

    0x00,                          /* block terminator                       */

    0x3B                           /* trailer                                */

};

‌static ngx_http_module_t  ngx_http_empty_gif_module_ctx = {

    NULL,                          /* preconfiguration */

    NULL,                          /* 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 */

};

‌ngx_module_t  ngx_http_empty_gif_module = {

    NGX_MODULE_V1,

    &ngx_http_empty_gif_module_ctx, /* module context */

    ngx_http_empty_gif_commands,   /* module directives */

    NGX_HTTP_MODULE,               /* module type */

    NULL,                          /* init master */

    NULL,                          /* init module */

    NULL,                          /* init process */

    NULL,                          /* init thread */

    NULL,                          /* exit thread */

    NULL,                          /* exit process */

    NULL,                          /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  ngx_http_gif_type = ngx_string("image/gif");

static ngx_int_t

‌ngx_http_empty_gif_handler(ngx_http_request_t *r)

{

    ngx_http_complex_value_t  cv;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_HTTP_NOT_ALLOWED;

    }

    ngx_memzero(&cv, sizeof(ngx_http_complex_value_t));

    cv.value.len = sizeof(ngx_empty_gif);

    cv.value.data = ngx_empty_gif;

    r->headers_out.last_modified_time = 23349600;

    return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv);

}

static char *

‌ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_empty_gif_handler;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_fastcgi_module.c - nginx-1.7.9

Global variables defined

ngx_http_fastcgi_cache_headers

ngx_http_fastcgi_commands

ngx_http_fastcgi_hide_headers

ngx_http_fastcgi_lowat_post

ngx_http_fastcgi_module

ngx_http_fastcgi_module

ngx_http_fastcgi_module_ctx

ngx_http_fastcgi_next_upstream_masks

ngx_http_fastcgi_request_start

ngx_http_fastcgi_temp_path

ngx_http_fastcgi_vars

Data types defined

ngx_http_fastcgi_begin_request_t

ngx_http_fastcgi_ctx_t

ngx_http_fastcgi_header_small_t

ngx_http_fastcgi_header_t

ngx_http_fastcgi_loc_conf_t

ngx_http_fastcgi_main_conf_t

ngx_http_fastcgi_params_t

ngx_http_fastcgi_request_start_t

ngx_http_fastcgi_split_part_t

ngx_http_fastcgi_state_e

Functions defined

ngx_http_fastcgi_abort_request

ngx_http_fastcgi_add_variables

ngx_http_fastcgi_cache

ngx_http_fastcgi_cache_key

ngx_http_fastcgi_create_key

ngx_http_fastcgi_create_loc_conf

ngx_http_fastcgi_create_main_conf

ngx_http_fastcgi_create_request

ngx_http_fastcgi_eval

ngx_http_fastcgi_finalize_request

ngx_http_fastcgi_handler

ngx_http_fastcgi_init_params

ngx_http_fastcgi_input_filter

ngx_http_fastcgi_input_filter_init

ngx_http_fastcgi_lowat_check

ngx_http_fastcgi_merge_loc_conf

ngx_http_fastcgi_non_buffered_filter

ngx_http_fastcgi_pass

ngx_http_fastcgi_path_info_variable

ngx_http_fastcgi_process_header

ngx_http_fastcgi_process_record

ngx_http_fastcgi_reinit_request

ngx_http_fastcgi_script_name_variable

ngx_http_fastcgi_split

ngx_http_fastcgi_split_path_info

ngx_http_fastcgi_store

Macros defined

NGX_HTTP_FASTCGI_ABORT_REQUEST

NGX_HTTP_FASTCGI_BEGIN_REQUEST

NGX_HTTP_FASTCGI_DATA

NGX_HTTP_FASTCGI_END_REQUEST

NGX_HTTP_FASTCGI_KEEP_CONN

NGX_HTTP_FASTCGI_PARAMS

NGX_HTTP_FASTCGI_RESPONDER

NGX_HTTP_FASTCGI_STDERR

NGX_HTTP_FASTCGI_STDIN

NGX_HTTP_FASTCGI_STDOUT

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_array_t                    caches;  /* ngx_http_file_cache_t * */

‌} ngx_http_fastcgi_main_conf_t;

typedef struct {

    ngx_array_t                   *flushes;

    ngx_array_t                   *lengths;

    ngx_array_t                   *values;

    ngx_uint_t                     number;

    ngx_hash_t                     hash;

‌} ngx_http_fastcgi_params_t;

typedef struct {

    ngx_http_upstream_conf_t       upstream;

    ngx_str_t                      index;

    ngx_http_fastcgi_params_t      params;

#if (NGX_HTTP_CACHE)

    ngx_http_fastcgi_params_t      params_cache;

#endif

    ngx_array_t                   *params_source;

    ngx_array_t                   *catch_stderr;

    ngx_array_t                   *fastcgi_lengths;

    ngx_array_t                   *fastcgi_values;

    ngx_flag_t                     keep_conn;

#if (NGX_HTTP_CACHE)

    ngx_http_complex_value_t       cache_key;

#endif

#if (NGX_PCRE)

    ngx_regex_t                   *split_regex;

    ngx_str_t                      split_name;

#endif

‌} ngx_http_fastcgi_loc_conf_t;

typedef enum {

    ngx_http_fastcgi_st_version = 0,

    ngx_http_fastcgi_st_type,

    ngx_http_fastcgi_st_request_id_hi,

    ngx_http_fastcgi_st_request_id_lo,

    ngx_http_fastcgi_st_content_length_hi,

    ngx_http_fastcgi_st_content_length_lo,

    ngx_http_fastcgi_st_padding_length,

    ngx_http_fastcgi_st_reserved,

    ngx_http_fastcgi_st_data,

    ngx_http_fastcgi_st_padding

‌} ngx_http_fastcgi_state_e;

typedef struct {

    u_char                        *start;

    u_char                        *end;

‌} ngx_http_fastcgi_split_part_t;

typedef struct {

    ngx_http_fastcgi_state_e       state;

    u_char                        *pos;

    u_char                        *last;

    ngx_uint_t                     type;

    size_t                         length;

    size_t                         padding;

    unsigned                       fastcgi_stdout:1;

    unsigned                       large_stderr:1;

    ngx_array_t                   *split_parts;

    ngx_str_t                      script_name;

    ngx_str_t                      path_info;

‌} ngx_http_fastcgi_ctx_t;

‌#define NGX_HTTP_FASTCGI_RESPONDER      1

‌#define NGX_HTTP_FASTCGI_KEEP_CONN      1

‌#define NGX_HTTP_FASTCGI_BEGIN_REQUEST  1

‌#define NGX_HTTP_FASTCGI_ABORT_REQUEST  2

‌#define NGX_HTTP_FASTCGI_END_REQUEST    3

‌#define NGX_HTTP_FASTCGI_PARAMS         4

‌#define NGX_HTTP_FASTCGI_STDIN          5

‌#define NGX_HTTP_FASTCGI_STDOUT         6

‌#define NGX_HTTP_FASTCGI_STDERR         7

‌#define NGX_HTTP_FASTCGI_DATA           8

typedef struct {

    u_char  version;

    u_char  type;

    u_char  request_id_hi;

    u_char  request_id_lo;

    u_char  content_length_hi;

    u_char  content_length_lo;

    u_char  padding_length;

    u_char  reserved;

‌} ngx_http_fastcgi_header_t;

typedef struct {

    u_char  role_hi;

    u_char  role_lo;

    u_char  flags;

    u_char  reserved[5];

‌} ngx_http_fastcgi_begin_request_t;

typedef struct {

    u_char  version;

    u_char  type;

    u_char  request_id_hi;

    u_char  request_id_lo;

‌} ngx_http_fastcgi_header_small_t;

typedef struct {

    ngx_http_fastcgi_header_t         h0;

    ngx_http_fastcgi_begin_request_t  br;

    ngx_http_fastcgi_header_small_t   h1;

‌} ngx_http_fastcgi_request_start_t;

static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,

    ngx_http_fastcgi_loc_conf_t *flcf);

#if (NGX_HTTP_CACHE)

static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);

#endif

static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);

static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,

    ngx_buf_t *buf);

static ngx_int_t ngx_http_fastcgi_non_buffered_filter(void *data,

    ssize_t bytes);

static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,

    ngx_http_fastcgi_ctx_t *f);

static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);

static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,

    ngx_int_t rc);

static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);

static void *ngx_http_fastcgi_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_fastcgi_init_params(ngx_conf_t *cf,

    ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_params_t *params,

    ngx_keyval_t *default_params);

static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,

    ngx_http_fastcgi_loc_conf_t *flcf);

static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,

    ngx_command_t *cmd, void *conf);

static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#if (NGX_HTTP_CACHE)

static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,

    void *data);

‌static ngx_conf_post_t  ngx_http_fastcgi_lowat_post =

    { ngx_http_fastcgi_lowat_check };

‌static ngx_conf_bitmask_t  ngx_http_fastcgi_next_upstream_masks[] = {

    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },

    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },

    { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },

    { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },

    { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },

    { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },

    { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },

    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },

    { ngx_null_string, 0 }

};

‌ngx_module_t  ngx_http_fastcgi_module;

‌static ngx_command_t  ngx_http_fastcgi_commands[] = {

    { ngx_string("fastcgi_pass"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_fastcgi_pass,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("fastcgi_index"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, index),

      NULL },

    { ngx_string("fastcgi_split_path_info"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_fastcgi_split_path_info,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("fastcgi_store"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_fastcgi_store,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("fastcgi_store_access"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_conf_set_access_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),

      NULL },

    { ngx_string("fastcgi_buffering"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),

      NULL },

    { ngx_string("fastcgi_ignore_client_abort"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),

      NULL },

    { ngx_string("fastcgi_bind"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_bind_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),

      NULL },

    { ngx_string("fastcgi_connect_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),

      NULL },

    { ngx_string("fastcgi_send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),

      NULL },

    { ngx_string("fastcgi_send_lowat"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),

      &ngx_http_fastcgi_lowat_post },

    { ngx_string("fastcgi_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),

      NULL },

    { ngx_string("fastcgi_pass_request_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),

      NULL },

    { ngx_string("fastcgi_pass_request_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),

      NULL },

    { ngx_string("fastcgi_intercept_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),

      NULL },

    { ngx_string("fastcgi_read_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),

      NULL },

    { ngx_string("fastcgi_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),

      NULL },

    { ngx_string("fastcgi_busy_buffers_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),

      NULL },

    { ngx_string("fastcgi_force_ranges"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.force_ranges),

      NULL },

    { ngx_string("fastcgi_limit_rate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.limit_rate),

      NULL },

#if (NGX_HTTP_CACHE)

    { ngx_string("fastcgi_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_fastcgi_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("fastcgi_cache_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_fastcgi_cache_key,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("fastcgi_cache_path"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,

      ngx_http_file_cache_set_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_main_conf_t, caches),

      &ngx_http_fastcgi_module },

    { ngx_string("fastcgi_cache_bypass"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_bypass),

      NULL },

    { ngx_string("fastcgi_no_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.no_cache),

      NULL },

    { ngx_string("fastcgi_cache_valid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_file_cache_valid_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),

      NULL },

    { ngx_string("fastcgi_cache_min_uses"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses),

      NULL },

    { ngx_string("fastcgi_cache_use_stale"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale),

      &ngx_http_fastcgi_next_upstream_masks },

    { ngx_string("fastcgi_cache_methods"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods),

      &ngx_http_upstream_cache_method_mask },

    { ngx_string("fastcgi_cache_lock"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock),

      NULL },

    { ngx_string("fastcgi_cache_lock_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout),

      NULL },

    { ngx_string("fastcgi_cache_lock_age"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_age),

      NULL },

    { ngx_string("fastcgi_cache_revalidate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_revalidate),

      NULL },

#endif

    { ngx_string("fastcgi_temp_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_conf_set_path_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),

      NULL },

    { ngx_string("fastcgi_max_temp_file_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),

      NULL },

    { ngx_string("fastcgi_temp_file_write_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),

      NULL },

    { ngx_string("fastcgi_next_upstream"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),

      &ngx_http_fastcgi_next_upstream_masks },

    { ngx_string("fastcgi_next_upstream_tries"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_tries),

      NULL },

    { ngx_string("fastcgi_next_upstream_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_timeout),

      NULL },

    { ngx_string("fastcgi_param"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,

      ngx_http_upstream_param_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, params_source),

      NULL },

    { ngx_string("fastcgi_pass_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),

      NULL },

    { ngx_string("fastcgi_hide_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),

      NULL },

    { ngx_string("fastcgi_ignore_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),

      &ngx_http_upstream_ignore_headers_masks },

    { ngx_string("fastcgi_catch_stderr"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),

      NULL },

    { ngx_string("fastcgi_keep_conn"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_fastcgi_module_ctx = {

    ngx_http_fastcgi_add_variables,        /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_fastcgi_create_main_conf,     /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_fastcgi_create_loc_conf,      /* create location configuration */

    ngx_http_fastcgi_merge_loc_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_fastcgi_module = {

    NGX_MODULE_V1,

    &ngx_http_fastcgi_module_ctx,          /* module context */

    ngx_http_fastcgi_commands,             /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {

    { 1,                                               /* version */

      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */

      0,                                               /* request_id_hi */

      1,                                               /* request_id_lo */

      0,                                               /* content_length_hi */

      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */

      0,                                               /* padding_length */

      0 },                                             /* reserved */

    { 0,                                               /* role_hi */

      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */

      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */

      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */

    { 1,                                               /* version */

      NGX_HTTP_FASTCGI_PARAMS,                         /* type */

      0,                                               /* request_id_hi */

      1 },                                             /* request_id_lo */

};

‌static ngx_http_variable_t  ngx_http_fastcgi_vars[] = {

    { ngx_string("fastcgi_script_name"), NULL,

      ngx_http_fastcgi_script_name_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("fastcgi_path_info"), NULL,

      ngx_http_fastcgi_path_info_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {

    ngx_string("Status"),

    ngx_string("X-Accel-Expires"),

    ngx_string("X-Accel-Redirect"),

    ngx_string("X-Accel-Limit-Rate"),

    ngx_string("X-Accel-Buffering"),

    ngx_string("X-Accel-Charset"),

    ngx_null_string

};

#if (NGX_HTTP_CACHE)

‌static ngx_keyval_t  ngx_http_fastcgi_cache_headers[] = {

    { ngx_string("HTTP_IF_MODIFIED_SINCE"),

      ngx_string("$upstream_cache_last_modified") },

    { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },

    { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },

    { ngx_string("HTTP_IF_MATCH"), ngx_string("") },

    { ngx_string("HTTP_RANGE"), ngx_string("") },

    { ngx_string("HTTP_IF_RANGE"), ngx_string("") },

    { ngx_null_string, ngx_null_string }

};

#endif

‌static ngx_path_init_t  ngx_http_fastcgi_temp_path = {

    ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }

};

static ngx_int_t

‌ngx_http_fastcgi_handler(ngx_http_request_t *r)

{

    ngx_int_t                      rc;

    ngx_http_upstream_t           *u;

    ngx_http_fastcgi_ctx_t        *f;

    ngx_http_fastcgi_loc_conf_t   *flcf;

#if (NGX_HTTP_CACHE)

    ngx_http_fastcgi_main_conf_t  *fmcf;

#endif

    if (ngx_http_upstream_create(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));

    if (f == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    if (flcf->fastcgi_lengths) {

        if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    u = r->upstream;

    ngx_str_set(&u->schema, "fastcgi://");

    u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;

    u->conf = &flcf->upstream;

#if (NGX_HTTP_CACHE)

    fmcf = ngx_http_get_module_main_conf(r, ngx_http_fastcgi_module);

    u->caches = &fmcf->caches;

    u->create_key = ngx_http_fastcgi_create_key;

#endif

    u->create_request = ngx_http_fastcgi_create_request;

    u->reinit_request = ngx_http_fastcgi_reinit_request;

    u->process_header = ngx_http_fastcgi_process_header;

    u->abort_request = ngx_http_fastcgi_abort_request;

    u->finalize_request = ngx_http_fastcgi_finalize_request;

    r->state = 0;

    u->buffering = flcf->upstream.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));

    if (u->pipe == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    u->pipe->input_filter = ngx_http_fastcgi_input_filter;

    u->pipe->input_ctx = r;

    u->input_filter_init = ngx_http_fastcgi_input_filter_init;

    u->input_filter = ngx_http_fastcgi_non_buffered_filter;

    u->input_filter_ctx = r;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        return rc;

    }

    return NGX_DONE;

}

static ngx_int_t

‌ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)

{

    ngx_url_t             url;

    ngx_http_upstream_t  *u;

    ngx_memzero(&url, sizeof(ngx_url_t));

    if (ngx_http_script_run(r, &url.url, flcf->fastcgi_lengths->elts, 0,

                            flcf->fastcgi_values->elts)

        == NULL)

    {

        return NGX_ERROR;

    }

    url.no_resolve = 1;

    if (ngx_parse_url(r->pool, &url) != NGX_OK) {

         if (url.err) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "%s in upstream \"%V\"", url.err, &url.url);

        }

        return NGX_ERROR;

    }

    u = r->upstream;

    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));

    if (u->resolved == NULL) {

        return NGX_ERROR;

    }

    if (url.addrs && url.addrs[0].sockaddr) {

        u->resolved->sockaddr = url.addrs[0].sockaddr;

        u->resolved->socklen = url.addrs[0].socklen;

        u->resolved->naddrs = 1;

        u->resolved->host = url.addrs[0].name;

    } else {

        u->resolved->host = url.host;

        u->resolved->port = url.port;

        u->resolved->no_port = url.no_port;

    }

    return NGX_OK;

}

#if (NGX_HTTP_CACHE)

static ngx_int_t

‌ngx_http_fastcgi_create_key(ngx_http_request_t *r)

{

    ngx_str_t                    *key;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    key = ngx_array_push(&r->cache->keys);

    if (key == NULL) {

        return NGX_ERROR;

    }

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_fastcgi_create_request(ngx_http_request_t *r)

{

    off_t                         file_pos;

    u_char                        ch, *pos, *lowcase_key;

    size_t                        size, len, key_len, val_len, padding,

                                  allocated;

    ngx_uint_t                    i, n, next, hash, skip_empty, header_params;

    ngx_buf_t                    *b;

    ngx_chain_t                  *cl, *body;

    ngx_list_part_t              *part;

    ngx_table_elt_t              *header, **ignored;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t      e, le;

    ngx_http_fastcgi_header_t    *h;

    ngx_http_fastcgi_params_t    *params;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    ngx_http_script_len_code_pt   lcode;

    len = 0;

    header_params = 0;

    ignored = NULL;

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

#if (NGX_HTTP_CACHE)

    params = r->upstream->cacheable ? &flcf->params_cache : &flcf->params;

#else

    params = &flcf->params;

#endif

    if (params->lengths) {

        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);

        le.flushed = 1;

        le.ip = params->lengths->elts;

        le.request = r;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            key_len = lcode(&le);

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                continue;

            }

            len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;

        }

    }

    if (flcf->upstream.pass_request_headers) {

        allocated = 0;

        lowcase_key = NULL;

        if (params->number) {

            n = 0;

            part = &r->headers_in.headers.part;

            while (part) {

                n += part->nelts;

                part = part->next;

            }

            ignored = ngx_palloc(r->pool, n * sizeof(void *));

            if (ignored == NULL) {

                return NGX_ERROR;

            }

        }

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (params->number) {

                if (allocated < header[i].key.len) {

                    allocated = header[i].key.len + 16;

                    lowcase_key = ngx_pnalloc(r->pool, allocated);

                    if (lowcase_key == NULL) {

                        return NGX_ERROR;

                    }

                }

                hash = 0;

                for (n = 0; n < header[i].key.len; n++) {

                    ch = header[i].key.data[n];

                    if (ch >= 'A' && ch <= 'Z') {

                        ch |= 0x20;

                    } else if (ch == '-') {

                        ch = '_';

                    }

                    hash = ngx_hash(hash, ch);

                    lowcase_key[n] = ch;

                }

                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {

                    ignored[header_params++] = &header[i];

                    continue;

                }

                n += sizeof("HTTP_") - 1;

            } else {

                n = sizeof("HTTP_") - 1 + header[i].key.len;

            }

            len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)

                + n + header[i].value.len;

        }

    }

    if (len > 65535) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "fastcgi request record is too big: %uz", len);

        return NGX_ERROR;

    }

    padding = 8 - len % 8;

    padding = (padding == 8) ? 0 : padding;

    size = sizeof(ngx_http_fastcgi_header_t)

           + sizeof(ngx_http_fastcgi_begin_request_t)

           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */

           + len + padding

           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */

           + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */

    b = ngx_create_temp_buf(r->pool, size);

    if (b == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    ngx_http_fastcgi_request_start.br.flags =

        flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;

    ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,

               sizeof(ngx_http_fastcgi_request_start_t));

    h = (ngx_http_fastcgi_header_t *)

             (b->pos + sizeof(ngx_http_fastcgi_header_t)

                     + sizeof(ngx_http_fastcgi_begin_request_t));

    h->content_length_hi = (u_char) ((len >> 8) & 0xff);

    h->content_length_lo = (u_char) (len & 0xff);

    h->padding_length = (u_char) padding;

    h->reserved = 0;

    b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)

                     + sizeof(ngx_http_fastcgi_begin_request_t)

                     + sizeof(ngx_http_fastcgi_header_t);

    if (params->lengths) {

        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

        e.ip = params->values->elts;

        e.pos = b->last;

        e.request = r;

        e.flushed = 1;

        le.ip = params->lengths->elts;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            key_len = (u_char) lcode(&le);

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                e.skip = 1;

                while (*(uintptr_t *) e.ip) {

                    code = *(ngx_http_script_code_pt *) e.ip;

                    code((ngx_http_script_engine_t *) &e);

                }

                e.ip += sizeof(uintptr_t);

                e.skip = 0;

                continue;

            }

            *e.pos++ = (u_char) key_len;

            if (val_len > 127) {

                *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);

                *e.pos++ = (u_char) ((val_len >> 16) & 0xff);

                *e.pos++ = (u_char) ((val_len >> 8) & 0xff);

                *e.pos++ = (u_char) (val_len & 0xff);

            } else {

                *e.pos++ = (u_char) val_len;

            }

            while (*(uintptr_t *) e.ip) {

                code = *(ngx_http_script_code_pt *) e.ip;

                code((ngx_http_script_engine_t *) &e);

            }

            e.ip += sizeof(uintptr_t);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "fastcgi param: \"%*s: %*s\"",

                           key_len, e.pos - (key_len + val_len),

                           val_len, e.pos - val_len);

        }

        b->last = e.pos;

    }

    if (flcf->upstream.pass_request_headers) {

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            for (n = 0; n < header_params; n++) {

                if (&header[i] == ignored[n]) {

                    goto next;

                }

            }

            key_len = sizeof("HTTP_") - 1 + header[i].key.len;

            if (key_len > 127) {

                *b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);

                *b->last++ = (u_char) ((key_len >> 16) & 0xff);

                *b->last++ = (u_char) ((key_len >> 8) & 0xff);

                *b->last++ = (u_char) (key_len & 0xff);

            } else {

                *b->last++ = (u_char) key_len;

            }

            val_len = header[i].value.len;

            if (val_len > 127) {

                *b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);

                *b->last++ = (u_char) ((val_len >> 16) & 0xff);

                *b->last++ = (u_char) ((val_len >> 8) & 0xff);

                *b->last++ = (u_char) (val_len & 0xff);

            } else {

                *b->last++ = (u_char) val_len;

            }

            b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);

            for (n = 0; n < header[i].key.len; n++) {

                ch = header[i].key.data[n];

                if (ch >= 'a' && ch <= 'z') {

                    ch &= ~0x20;

                } else if (ch == '-') {

                    ch = '_';

                }

                *b->last++ = ch;

            }

            b->last = ngx_copy(b->last, header[i].value.data, val_len);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "fastcgi param: \"%*s: %*s\"",

                           key_len, b->last - (key_len + val_len),

                           val_len, b->last - val_len);

        next:

            continue;

        }

    }

    if (padding) {

        ngx_memzero(b->last, padding);

        b->last += padding;

    }

    h = (ngx_http_fastcgi_header_t *) b->last;

    b->last += sizeof(ngx_http_fastcgi_header_t);

    h->version = 1;

    h->type = NGX_HTTP_FASTCGI_PARAMS;

    h->request_id_hi = 0;

    h->request_id_lo = 1;

    h->content_length_hi = 0;

    h->content_length_lo = 0;

    h->padding_length = 0;

    h->reserved = 0;

    h = (ngx_http_fastcgi_header_t *) b->last;

    b->last += sizeof(ngx_http_fastcgi_header_t);

    if (flcf->upstream.pass_request_body) {

        body = r->upstream->request_bufs;

        r->upstream->request_bufs = cl;

#if (NGX_SUPPRESS_WARN)

        file_pos = 0;

        pos = NULL;

#endif

        while (body) {

            if (body->buf->in_file) {

                file_pos = body->buf->file_pos;

            } else {

                pos = body->buf->pos;

            }

            next = 0;

            do {

                b = ngx_alloc_buf(r->pool);

                if (b == NULL) {

                    return NGX_ERROR;

                }

                ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

                if (body->buf->in_file) {

                    b->file_pos = file_pos;

                    file_pos += 32 * 1024;

                    if (file_pos >= body->buf->file_last) {

                        file_pos = body->buf->file_last;

                        next = 1;

                    }

                    b->file_last = file_pos;

                    len = (ngx_uint_t) (file_pos - b->file_pos);

                } else {

                    b->pos = pos;

                    b->start = pos;

                    pos += 32 * 1024;

                    if (pos >= body->buf->last) {

                        pos = body->buf->last;

                        next = 1;

                    }

                    b->last = pos;

                    len = (ngx_uint_t) (pos - b->pos);

                }

                padding = 8 - len % 8;

                padding = (padding == 8) ? 0 : padding;

                h->version = 1;

                h->type = NGX_HTTP_FASTCGI_STDIN;

                h->request_id_hi = 0;

                h->request_id_lo = 1;

                h->content_length_hi = (u_char) ((len >> 8) & 0xff);

                h->content_length_lo = (u_char) (len & 0xff);

                h->padding_length = (u_char) padding;

                h->reserved = 0;

                cl->next = ngx_alloc_chain_link(r->pool);

                if (cl->next == NULL) {

                    return NGX_ERROR;

                }

                cl = cl->next;

                cl->buf = b;

                b = ngx_create_temp_buf(r->pool,

                                        sizeof(ngx_http_fastcgi_header_t)

                                        + padding);

                if (b == NULL) {

                    return NGX_ERROR;

                }

                if (padding) {

                    ngx_memzero(b->last, padding);

                    b->last += padding;

                }

                h = (ngx_http_fastcgi_header_t *) b->last;

                b->last += sizeof(ngx_http_fastcgi_header_t);

                cl->next = ngx_alloc_chain_link(r->pool);

                if (cl->next == NULL) {

                    return NGX_ERROR;

                }

                cl = cl->next;

                cl->buf = b;

            } while (!next);

            body = body->next;

        }

    } else {

        r->upstream->request_bufs = cl;

    }

    h->version = 1;

    h->type = NGX_HTTP_FASTCGI_STDIN;

    h->request_id_hi = 0;

    h->request_id_lo = 1;

    h->content_length_hi = 0;

    h->content_length_lo = 0;

    h->padding_length = 0;

    h->reserved = 0;

    cl->next = NULL;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)

{

    ngx_http_fastcgi_ctx_t  *f;

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    if (f == NULL) {

        return NGX_OK;

    }

    f->state = ngx_http_fastcgi_st_version;

    f->fastcgi_stdout = 0;

    f->large_stderr = 0;

    if (f->split_parts) {

        f->split_parts->nelts = 0;

    }

    r->state = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_process_header(ngx_http_request_t *r)

{

    u_char                         *p, *msg, *start, *last,

                                   *part_start, *part_end;

    size_t                          size;

    ngx_str_t                      *status_line, *pattern;

    ngx_int_t                       rc, status;

    ngx_buf_t                       buf;

    ngx_uint_t                      i;

    ngx_table_elt_t                *h;

    ngx_http_upstream_t            *u;

    ngx_http_fastcgi_ctx_t         *f;

    ngx_http_upstream_header_t     *hh;

    ngx_http_fastcgi_loc_conf_t    *flcf;

    ngx_http_fastcgi_split_part_t  *part;

    ngx_http_upstream_main_conf_t  *umcf;

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    u = r->upstream;

    for ( ;; ) {

        if (f->state < ngx_http_fastcgi_st_data) {

            f->pos = u->buffer.pos;

            f->last = u->buffer.last;

            rc = ngx_http_fastcgi_process_record(r, f);

            u->buffer.pos = f->pos;

            u->buffer.last = f->last;

            if (rc == NGX_AGAIN) {

                return NGX_AGAIN;

            }

            if (rc == NGX_ERROR) {

                return NGX_HTTP_UPSTREAM_INVALID_HEADER;

            }

            if (f->type != NGX_HTTP_FASTCGI_STDOUT

                && f->type != NGX_HTTP_FASTCGI_STDERR)

            {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream sent unexpected FastCGI record: %d",

                              f->type);

                return NGX_HTTP_UPSTREAM_INVALID_HEADER;

            }

            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream prematurely closed FastCGI stdout");

                return NGX_HTTP_UPSTREAM_INVALID_HEADER;

            }

        }

        if (f->state == ngx_http_fastcgi_st_padding) {

            if (u->buffer.pos + f->padding < u->buffer.last) {

                f->state = ngx_http_fastcgi_st_version;

                u->buffer.pos += f->padding;

                continue;

            }

            if (u->buffer.pos + f->padding == u->buffer.last) {

                f->state = ngx_http_fastcgi_st_version;

                u->buffer.pos = u->buffer.last;

                return NGX_AGAIN;

            }

            f->padding -= u->buffer.last - u->buffer.pos;

            u->buffer.pos = u->buffer.last;

            return NGX_AGAIN;

        }

        /* f->state == ngx_http_fastcgi_st_data */

        if (f->type == NGX_HTTP_FASTCGI_STDERR) {

            if (f->length) {

                msg = u->buffer.pos;

                if (u->buffer.pos + f->length <= u->buffer.last) {

                    u->buffer.pos += f->length;

                    f->length = 0;

                    f->state = ngx_http_fastcgi_st_padding;

                } else {

                    f->length -= u->buffer.last - u->buffer.pos;

                    u->buffer.pos = u->buffer.last;

                }

                for (p = u->buffer.pos - 1; msg < p; p--) {

                    if (*p != LF && *p != CR && *p != '.' && *p != ' ') {

                        break;

                    }

                }

                p++;

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "FastCGI sent in stderr: \"%*s\"", p - msg, msg);

                flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

                if (flcf->catch_stderr) {

                    pattern = flcf->catch_stderr->elts;

                    for (i = 0; i < flcf->catch_stderr->nelts; i++) {

                        if (ngx_strnstr(msg, (char *) pattern[i].data,

                                        p - msg)

                            != NULL)

                        {

                            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

                        }

                    }

                }

                if (u->buffer.pos == u->buffer.last) {

                    if (!f->fastcgi_stdout) {

                        /*

                         * the special handling the large number

                         * of the PHP warnings to not allocate memory

                         */

#if (NGX_HTTP_CACHE)

                        if (r->cache) {

                            u->buffer.pos = u->buffer.start

                                                     + r->cache->header_start;

                        } else {

                            u->buffer.pos = u->buffer.start;

                        }

#else

                        u->buffer.pos = u->buffer.start;

#endif

                        u->buffer.last = u->buffer.pos;

                        f->large_stderr = 1;

                    }

                    return NGX_AGAIN;

                }

            } else {

                f->state = ngx_http_fastcgi_st_padding;

            }

            continue;

        }

        /* f->type == NGX_HTTP_FASTCGI_STDOUT */

#if (NGX_HTTP_CACHE)

        if (f->large_stderr && r->cache) {

            u_char                     *start;

            ssize_t                     len;

            ngx_http_fastcgi_header_t  *fh;

            start = u->buffer.start + r->cache->header_start;

            len = u->buffer.pos - start - 2 * sizeof(ngx_http_fastcgi_header_t);

            /*

             * A tail of large stderr output before HTTP header is placed

             * in a cache file without a FastCGI record header.

             * To workaround it we put a dummy FastCGI record header at the

             * start of the stderr output or update r->cache_header_start,

             * if there is no enough place for the record header.

             */

            if (len >= 0) {

                fh = (ngx_http_fastcgi_header_t *) start;

                fh->version = 1;

                fh->type = NGX_HTTP_FASTCGI_STDERR;

                fh->request_id_hi = 0;

                fh->request_id_lo = 1;

                fh->content_length_hi = (u_char) ((len >> 8) & 0xff);

                fh->content_length_lo = (u_char) (len & 0xff);

                fh->padding_length = 0;

                fh->reserved = 0;

            } else {

                r->cache->header_start += u->buffer.pos - start

                                           - sizeof(ngx_http_fastcgi_header_t);

            }

            f->large_stderr = 0;

        }

#endif

        f->fastcgi_stdout = 1;

        start = u->buffer.pos;

        if (u->buffer.pos + f->length < u->buffer.last) {

            /*

             * set u->buffer.last to the end of the FastCGI record data

             * for ngx_http_parse_header_line()

             */

            last = u->buffer.last;

            u->buffer.last = u->buffer.pos + f->length;

        } else {

            last = NULL;

        }

        for ( ;; ) {

            part_start = u->buffer.pos;

            part_end = u->buffer.last;

            rc = ngx_http_parse_header_line(r, &u->buffer, 1);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http fastcgi parser: %d", rc);

            if (rc == NGX_AGAIN) {

                break;

            }

            if (rc == NGX_OK) {

                /* a header line has been parsed successfully */

                h = ngx_list_push(&u->headers_in.headers);

                if (h == NULL) {

                    return NGX_ERROR;

                }

                if (f->split_parts && f->split_parts->nelts) {

                    part = f->split_parts->elts;

                    size = u->buffer.pos - part_start;

                    for (i = 0; i < f->split_parts->nelts; i++) {

                        size += part[i].end - part[i].start;

                    }

                    p = ngx_pnalloc(r->pool, size);

                    if (p == NULL) {

                        return NGX_ERROR;

                    }

                    buf.pos = p;

                    for (i = 0; i < f->split_parts->nelts; i++) {

                        p = ngx_cpymem(p, part[i].start,

                                       part[i].end - part[i].start);

                    }

                    p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);

                    buf.last = p;

                    f->split_parts->nelts = 0;

                    rc = ngx_http_parse_header_line(r, &buf, 1);

                    if (rc != NGX_OK) {

                        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                                      "invalid header after joining "

                                      "FastCGI records");

                        return NGX_ERROR;

                    }

                    h->key.len = r->header_name_end - r->header_name_start;

                    h->key.data = r->header_name_start;

                    h->key.data[h->key.len] = '\0';

                    h->value.len = r->header_end - r->header_start;

                    h->value.data = r->header_start;

                    h->value.data[h->value.len] = '\0';

                    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);

                    if (h->lowcase_key == NULL) {

                        return NGX_ERROR;

                    }

                } else {

                    h->key.len = r->header_name_end - r->header_name_start;

                    h->value.len = r->header_end - r->header_start;

                    h->key.data = ngx_pnalloc(r->pool,

                                              h->key.len + 1 + h->value.len + 1

                                              + h->key.len);

                    if (h->key.data == NULL) {

                        return NGX_ERROR;

                    }

                    h->value.data = h->key.data + h->key.len + 1;

                    h->lowcase_key = h->key.data + h->key.len + 1

                                     + h->value.len + 1;

                    ngx_memcpy(h->key.data, r->header_name_start, h->key.len);

                    h->key.data[h->key.len] = '\0';

                    ngx_memcpy(h->value.data, r->header_start, h->value.len);

                    h->value.data[h->value.len] = '\0';

                }

                h->hash = r->header_hash;

                if (h->key.len == r->lowcase_index) {

                    ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

                } else {

                    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);

                }

                hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,

                                   h->lowcase_key, h->key.len);

                if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {

                    return NGX_ERROR;

                }

                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "http fastcgi header: \"%V: %V\"",

                               &h->key, &h->value);

                if (u->buffer.pos < u->buffer.last) {

                    continue;

                }

                /* the end of the FastCGI record */

                break;

            }

            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

                /* a whole header has been parsed successfully */

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "http fastcgi header done");

                if (u->headers_in.status) {

                    status_line = &u->headers_in.status->value;

                    status = ngx_atoi(status_line->data, 3);

                    if (status == NGX_ERROR) {

                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                      "upstream sent invalid status \"%V\"",

                                      status_line);

                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;

                    }

                    u->headers_in.status_n = status;

                    u->headers_in.status_line = *status_line;

                } else if (u->headers_in.location) {

                    u->headers_in.status_n = 302;

                    ngx_str_set(&u->headers_in.status_line,

                                "302 Moved Temporarily");

                } else {

                    u->headers_in.status_n = 200;

                    ngx_str_set(&u->headers_in.status_line, "200 OK");

                }

                if (u->state && u->state->status == 0) {

                    u->state->status = u->headers_in.status_n;

                }

                break;

            }

            /* there was error while a header line parsing */

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "upstream sent invalid header");

            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

        }

        if (last) {

            u->buffer.last = last;

        }

        f->length -= u->buffer.pos - start;

        if (f->length == 0) {

            f->state = ngx_http_fastcgi_st_padding;

        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            return NGX_OK;

        }

        if (rc == NGX_OK) {

            continue;

        }

        /* rc == NGX_AGAIN */

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "upstream split a header line in FastCGI records");

        if (f->split_parts == NULL) {

            f->split_parts = ngx_array_create(r->pool, 1,

                                        sizeof(ngx_http_fastcgi_split_part_t));

            if (f->split_parts == NULL) {

                return NGX_ERROR;

            }

        }

        part = ngx_array_push(f->split_parts);

        if (part == NULL) {

            return NGX_ERROR;

        }

        part->start = part_start;

        part->end = part_end;

        if (u->buffer.pos < u->buffer.last) {

            continue;

        }

        return NGX_AGAIN;

    }

}

static ngx_int_t

‌ngx_http_fastcgi_input_filter_init(void *data)

{

    ngx_http_request_t           *r = data;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    r->upstream->pipe->length = flcf->keep_conn ?

                                (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)

{

    u_char                       *m, *msg;

    ngx_int_t                     rc;

    ngx_buf_t                    *b, **prev;

    ngx_chain_t                  *cl;

    ngx_http_request_t           *r;

    ngx_http_fastcgi_ctx_t       *f;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    if (buf->pos == buf->last) {

        return NGX_OK;

    }

    r = p->input_ctx;

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    b = NULL;

    prev = &buf->shadow;

    f->pos = buf->pos;

    f->last = buf->last;

    for ( ;; ) {

        if (f->state < ngx_http_fastcgi_st_data) {

            rc = ngx_http_fastcgi_process_record(r, f);

            if (rc == NGX_AGAIN) {

                break;

            }

            if (rc == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {

                f->state = ngx_http_fastcgi_st_padding;

                if (!flcf->keep_conn) {

                    p->upstream_done = 1;

                }

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,

                               "http fastcgi closed stdout");

                continue;

            }

            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,

                               "http fastcgi sent end request");

                if (!flcf->keep_conn) {

                    p->upstream_done = 1;

                    break;

                }

                continue;

            }

        }

        if (f->state == ngx_http_fastcgi_st_padding) {

            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {

                if (f->pos + f->padding < f->last) {

                    p->upstream_done = 1;

                    break;

                }

                if (f->pos + f->padding == f->last) {

                    p->upstream_done = 1;

                    r->upstream->keepalive = 1;

                    break;

                }

                f->padding -= f->last - f->pos;

                break;

            }

            if (f->pos + f->padding < f->last) {

                f->state = ngx_http_fastcgi_st_version;

                f->pos += f->padding;

                continue;

            }

            if (f->pos + f->padding == f->last) {

                f->state = ngx_http_fastcgi_st_version;

                break;

            }

            f->padding -= f->last - f->pos;

            break;

        }

        /* f->state == ngx_http_fastcgi_st_data */

        if (f->type == NGX_HTTP_FASTCGI_STDERR) {

            if (f->length) {

                if (f->pos == f->last) {

                    break;

                }

                msg = f->pos;

                if (f->pos + f->length <= f->last) {

                    f->pos += f->length;

                    f->length = 0;

                    f->state = ngx_http_fastcgi_st_padding;

                } else {

                    f->length -= f->last - f->pos;

                    f->pos = f->last;

                }

                for (m = f->pos - 1; msg < m; m--) {

                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {

                        break;

                    }

                }

                ngx_log_error(NGX_LOG_ERR, p->log, 0,

                              "FastCGI sent in stderr: \"%*s\"",

                              m + 1 - msg, msg);

            } else {

                f->state = ngx_http_fastcgi_st_padding;

            }

            continue;

        }

        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {

            if (f->pos + f->length <= f->last) {

                f->state = ngx_http_fastcgi_st_padding;

                f->pos += f->length;

                continue;

            }

            f->length -= f->last - f->pos;

            break;

        }

        /* f->type == NGX_HTTP_FASTCGI_STDOUT */

        if (f->pos == f->last) {

            break;

        }

        cl = ngx_chain_get_free_buf(p->pool, &p->free);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        b = cl->buf;

        ngx_memzero(b, sizeof(ngx_buf_t));

        b->pos = f->pos;

        b->start = buf->start;

        b->end = buf->end;

        b->tag = p->tag;

        b->temporary = 1;

        b->recycled = 1;

        *prev = b;

        prev = &b->shadow;

        if (p->in) {

            *p->last_in = cl;

        } else {

            p->in = cl;

        }

        p->last_in = &cl->next;

        /* STUB */ b->num = buf->num;

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "input buf #%d %p", b->num, b->pos);

        if (f->pos + f->length <= f->last) {

            f->state = ngx_http_fastcgi_st_padding;

            f->pos += f->length;

            b->last = f->pos;

            continue;

        }

        f->length -= f->last - f->pos;

        b->last = f->last;

        break;

    }

    if (flcf->keep_conn) {

        /* set p->length, minimal amount of data we want to see */

        if (f->state < ngx_http_fastcgi_st_data) {

            p->length = 1;

        } else if (f->state == ngx_http_fastcgi_st_padding) {

            p->length = f->padding;

        } else {

            /* ngx_http_fastcgi_st_data */

            p->length = f->length;

        }

    }

    if (b) {

        b->shadow = buf;

        b->last_shadow = 1;

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "input buf %p %z", b->pos, b->last - b->pos);

        return NGX_OK;

    }

    /* there is no data record in the buf, add it to free chain */

    if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)

{

    u_char                  *m, *msg;

    ngx_int_t                rc;

    ngx_buf_t               *b, *buf;

    ngx_chain_t             *cl, **ll;

    ngx_http_request_t      *r;

    ngx_http_upstream_t     *u;

    ngx_http_fastcgi_ctx_t  *f;

    r = data;

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    u = r->upstream;

    buf = &u->buffer;

    buf->pos = buf->last;

    buf->last += bytes;

    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {

        ll = &cl->next;

    }

    f->pos = buf->pos;

    f->last = buf->last;

    for ( ;; ) {

        if (f->state < ngx_http_fastcgi_st_data) {

            rc = ngx_http_fastcgi_process_record(r, f);

            if (rc == NGX_AGAIN) {

                break;

            }

            if (rc == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {

                f->state = ngx_http_fastcgi_st_padding;

                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "http fastcgi closed stdout");

                continue;

            }

        }

        if (f->state == ngx_http_fastcgi_st_padding) {

            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {

                if (f->pos + f->padding < f->last) {

                    u->length = 0;

                    break;

                }

                if (f->pos + f->padding == f->last) {

                    u->length = 0;

                    u->keepalive = 1;

                    break;

                }

                f->padding -= f->last - f->pos;

                break;

            }

            if (f->pos + f->padding < f->last) {

                f->state = ngx_http_fastcgi_st_version;

                f->pos += f->padding;

                continue;

            }

            if (f->pos + f->padding == f->last) {

                f->state = ngx_http_fastcgi_st_version;

                break;

            }

            f->padding -= f->last - f->pos;

            break;

        }

        /* f->state == ngx_http_fastcgi_st_data */

        if (f->type == NGX_HTTP_FASTCGI_STDERR) {

            if (f->length) {

                if (f->pos == f->last) {

                    break;

                }

                msg = f->pos;

                if (f->pos + f->length <= f->last) {

                    f->pos += f->length;

                    f->length = 0;

                    f->state = ngx_http_fastcgi_st_padding;

                } else {

                    f->length -= f->last - f->pos;

                    f->pos = f->last;

                }

                for (m = f->pos - 1; msg < m; m--) {

                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {

                        break;

                    }

                }

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "FastCGI sent in stderr: \"%*s\"",

                              m + 1 - msg, msg);

            } else {

                f->state = ngx_http_fastcgi_st_padding;

            }

            continue;

        }

        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {

            if (f->pos + f->length <= f->last) {

                f->state = ngx_http_fastcgi_st_padding;

                f->pos += f->length;

                continue;

            }

            f->length -= f->last - f->pos;

            break;

        }

        /* f->type == NGX_HTTP_FASTCGI_STDOUT */

        if (f->pos == f->last) {

            break;

        }

        cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        *ll = cl;

        ll = &cl->next;

        b = cl->buf;

        b->flush = 1;

        b->memory = 1;

        b->pos = f->pos;

        b->tag = u->output.tag;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http fastcgi output buf %p", b->pos);

        if (f->pos + f->length <= f->last) {

            f->state = ngx_http_fastcgi_st_padding;

            f->pos += f->length;

            b->last = f->pos;

            continue;

        }

        f->length -= f->last - f->pos;

        b->last = f->last;

        break;

    }

    /* provide continuous buffer for subrequests in memory */

    if (r->subrequest_in_memory) {

        cl = u->out_bufs;

        if (cl) {

            buf->pos = cl->buf->pos;

        }

        buf->last = buf->pos;

        for (cl = u->out_bufs; cl; cl = cl->next) {

            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http fastcgi in memory %p-%p %uz",

                           cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));

            if (buf->last == cl->buf->pos) {

                buf->last = cl->buf->last;

                continue;

            }

            buf->last = ngx_movemem(buf->last, cl->buf->pos,

                                    cl->buf->last - cl->buf->pos);

            cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);

            cl->buf->last = buf->last;

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_process_record(ngx_http_request_t *r,

    ngx_http_fastcgi_ctx_t *f)

{

    u_char                     ch, *p;

    ngx_http_fastcgi_state_e   state;

    state = f->state;

    for (p = f->pos; p < f->last; p++) {

        ch = *p;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http fastcgi record byte: %02Xd", ch);

        switch (state) {

        case ngx_http_fastcgi_st_version:

            if (ch != 1) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream sent unsupported FastCGI "

                              "protocol version: %d", ch);

                return NGX_ERROR;

            }

            state = ngx_http_fastcgi_st_type;

            break;

        case ngx_http_fastcgi_st_type:

            switch (ch) {

            case NGX_HTTP_FASTCGI_STDOUT:

            case NGX_HTTP_FASTCGI_STDERR:

            case NGX_HTTP_FASTCGI_END_REQUEST:

                 f->type = (ngx_uint_t) ch;

                 break;

            default:

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream sent invalid FastCGI "

                              "record type: %d", ch);

                return NGX_ERROR;

            }

            state = ngx_http_fastcgi_st_request_id_hi;

            break;

        /* we support the single request per connection */

        case ngx_http_fastcgi_st_request_id_hi:

            if (ch != 0) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream sent unexpected FastCGI "

                              "request id high byte: %d", ch);

                return NGX_ERROR;

            }

            state = ngx_http_fastcgi_st_request_id_lo;

            break;

        case ngx_http_fastcgi_st_request_id_lo:

            if (ch != 1) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                              "upstream sent unexpected FastCGI "

                              "request id low byte: %d", ch);

                return NGX_ERROR;

            }

            state = ngx_http_fastcgi_st_content_length_hi;

            break;

        case ngx_http_fastcgi_st_content_length_hi:

            f->length = ch << 8;

            state = ngx_http_fastcgi_st_content_length_lo;

            break;

        case ngx_http_fastcgi_st_content_length_lo:

            f->length |= (size_t) ch;

            state = ngx_http_fastcgi_st_padding_length;

            break;

        case ngx_http_fastcgi_st_padding_length:

            f->padding = (size_t) ch;

            state = ngx_http_fastcgi_st_reserved;

            break;

        case ngx_http_fastcgi_st_reserved:

            state = ngx_http_fastcgi_st_data;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http fastcgi record length: %z", f->length);

            f->pos = p + 1;

            f->state = state;

            return NGX_OK;

        /* suppress warning */

        case ngx_http_fastcgi_st_data:

        case ngx_http_fastcgi_st_padding:

            break;

        }

    }

    f->state = state;

    return NGX_AGAIN;

}

static void

‌ngx_http_fastcgi_abort_request(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "abort http fastcgi request");

    return;

}

static void

‌ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http fastcgi request");

    return;

}

static ngx_int_t

‌ngx_http_fastcgi_add_variables(ngx_conf_t *cf)

{

   ngx_http_variable_t  *var, *v;

    for (v = ngx_http_fastcgi_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static void *

‌ngx_http_fastcgi_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_fastcgi_main_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

#if (NGX_HTTP_CACHE)

    if (ngx_array_init(&conf->caches, cf->pool, 4,

                       sizeof(ngx_http_file_cache_t *))

        != NGX_OK)

    {

        return NULL;

    }

#endif

    return conf;

}

static void *

‌ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_fastcgi_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->upstream.bufs.num = 0;

     *     conf->upstream.ignore_headers = 0;

     *     conf->upstream.next_upstream = 0;

     *     conf->upstream.cache_zone = NULL;

     *     conf->upstream.cache_use_stale = 0;

     *     conf->upstream.cache_methods = 0;

     *     conf->upstream.temp_path = NULL;

     *     conf->upstream.hide_headers_hash = { NULL, 0 };

     *     conf->upstream.uri = { 0, NULL };

     *     conf->upstream.location = NULL;

     *     conf->upstream.store_lengths = NULL;

     *     conf->upstream.store_values = NULL;

     *

     *     conf->index.len = { 0, NULL };

     */

    conf->upstream.store = NGX_CONF_UNSET;

    conf->upstream.store_access = NGX_CONF_UNSET_UINT;

    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

    conf->upstream.buffering = NGX_CONF_UNSET;

    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;

    conf->upstream.force_ranges = NGX_CONF_UNSET;

    conf->upstream.local = NGX_CONF_UNSET_PTR;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;

    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;

    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.pass_request_headers = NGX_CONF_UNSET;

    conf->upstream.pass_request_body = NGX_CONF_UNSET;

#if (NGX_HTTP_CACHE)

    conf->upstream.cache = NGX_CONF_UNSET;

    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;

    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;

    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_lock = NGX_CONF_UNSET;

    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_revalidate = NGX_CONF_UNSET;

#endif

    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.intercept_errors = NGX_CONF_UNSET;

    /* "fastcgi_cyclic_temp_file" is disabled */

    conf->upstream.cyclic_temp_file = 0;

    conf->upstream.change_buffering = 1;

    conf->catch_stderr = NGX_CONF_UNSET_PTR;

    conf->keep_conn = NGX_CONF_UNSET;

    ngx_str_set(&conf->upstream.module, "fastcgi");

    return conf;

}

static char *

‌ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_fastcgi_loc_conf_t *prev = parent;

    ngx_http_fastcgi_loc_conf_t *conf = child;

    size_t                        size;

    ngx_int_t                     rc;

    ngx_hash_init_t               hash;

    ngx_http_core_loc_conf_t     *clcf;

#if (NGX_HTTP_CACHE)

    if (conf->upstream.store > 0) {

        conf->upstream.cache = 0;

    }

    if (conf->upstream.cache > 0) {

        conf->upstream.store = 0;

    }

#endif

    if (conf->upstream.store == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.store,

                              prev->upstream.store, 0);

        conf->upstream.store_lengths = prev->upstream.store_lengths;

        conf->upstream.store_values = prev->upstream.store_values;

    }

    ngx_conf_merge_uint_value(conf->upstream.store_access,

                              prev->upstream.store_access, 0600);

    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,

                              prev->upstream.next_upstream_tries, 0);

    ngx_conf_merge_value(conf->upstream.buffering,

                              prev->upstream.buffering, 1);

    ngx_conf_merge_value(conf->upstream.ignore_client_abort,

                              prev->upstream.ignore_client_abort, 0);

    ngx_conf_merge_value(conf->upstream.force_ranges,

                              prev->upstream.force_ranges, 0);

    ngx_conf_merge_ptr_value(conf->upstream.local,

                              prev->upstream.local, NULL);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,

                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,

                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,

                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,

                              prev->upstream.next_upstream_timeout, 0);

    ngx_conf_merge_size_value(conf->upstream.send_lowat,

                              prev->upstream.send_lowat, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,

                              prev->upstream.buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_size_value(conf->upstream.limit_rate,

                              prev->upstream.limit_rate, 0);

    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,

                              8, ngx_pagesize);

    if (conf->upstream.bufs.num < 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "there must be at least 2 \"fastcgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    size = conf->upstream.buffer_size;

    if (size < conf->upstream.bufs.size) {

        size = conf->upstream.bufs.size;

    }

    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,

                              prev->upstream.busy_buffers_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.busy_buffers_size = 2 * size;

    } else {

        conf->upstream.busy_buffers_size =

                                         conf->upstream.busy_buffers_size_conf;

    }

    if (conf->upstream.busy_buffers_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"fastcgi_busy_buffers_size\" must be equal to or greater than "

             "the maximum of the value of \"fastcgi_buffer_size\" and "

             "one of the \"fastcgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    if (conf->upstream.busy_buffers_size

        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"fastcgi_busy_buffers_size\" must be less than "

             "the size of all \"fastcgi_buffers\" minus one buffer");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,

                              prev->upstream.temp_file_write_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.temp_file_write_size = 2 * size;

    } else {

        conf->upstream.temp_file_write_size =

                                      conf->upstream.temp_file_write_size_conf;

    }

    if (conf->upstream.temp_file_write_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"fastcgi_temp_file_write_size\" must be equal to or greater "

             "than the maximum of the value of \"fastcgi_buffer_size\" and "

             "one of the \"fastcgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,

                              prev->upstream.max_temp_file_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;

    } else {

        conf->upstream.max_temp_file_size =

                                        conf->upstream.max_temp_file_size_conf;

    }

    if (conf->upstream.max_temp_file_size != 0

        && conf->upstream.max_temp_file_size < size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "

             "temporary files usage or must be equal to or greater than "

             "the maximum of the value of \"fastcgi_buffer_size\" and "

             "one of the \"fastcgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,

                              prev->upstream.ignore_headers,

                              NGX_CONF_BITMASK_SET);

    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,

                              prev->upstream.next_upstream,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_ERROR

                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET

                                       |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,

                              prev->upstream.temp_path,

                              &ngx_http_fastcgi_temp_path)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.cache,

                              prev->upstream.cache, 0);

        conf->upstream.cache_zone = prev->upstream.cache_zone;

        conf->upstream.cache_value = prev->upstream.cache_value;

    }

    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {

        ngx_shm_zone_t  *shm_zone;

        shm_zone = conf->upstream.cache_zone;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"fastcgi_cache\" zone \"%V\" is unknown",

                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,

                              prev->upstream.cache_min_uses, 1);

    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,

                              prev->upstream.cache_use_stale,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_OFF));

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET

                                         |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {

        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;

    }

    if (conf->upstream.cache_methods == 0) {

        conf->upstream.cache_methods = prev->upstream.cache_methods;

    }

    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;

    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,

                             prev->upstream.cache_bypass, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.no_cache,

                             prev->upstream.no_cache, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,

                             prev->upstream.cache_valid, NULL);

    if (conf->cache_key.value.data == NULL) {

        conf->cache_key = prev->cache_key;

    }

    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "no \"fastcgi_cache_key\" for \"fastcgi_cache\"");

    }

    ngx_conf_merge_value(conf->upstream.cache_lock,

                              prev->upstream.cache_lock, 0);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,

                              prev->upstream.cache_lock_timeout, 5000);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,

                              prev->upstream.cache_lock_age, 5000);

    ngx_conf_merge_value(conf->upstream.cache_revalidate,

                              prev->upstream.cache_revalidate, 0);

#endif

    ngx_conf_merge_value(conf->upstream.pass_request_headers,

                              prev->upstream.pass_request_headers, 1);

    ngx_conf_merge_value(conf->upstream.pass_request_body,

                              prev->upstream.pass_request_body, 1);

    ngx_conf_merge_value(conf->upstream.intercept_errors,

                              prev->upstream.intercept_errors, 0);

    ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);

    ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);

    ngx_conf_merge_str_value(conf->index, prev->index, "");

    hash.max_size = 512;

    hash.bucket_size = ngx_align(64, ngx_cacheline_size);

    hash.name = "fastcgi_hide_headers_hash";

    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,

             &prev->upstream, ngx_http_fastcgi_hide_headers, &hash)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    if (clcf->noname

        && conf->upstream.upstream == NULL && conf->fastcgi_lengths == NULL)

    {

        conf->upstream.upstream = prev->upstream.upstream;

        conf->fastcgi_lengths = prev->fastcgi_lengths;

        conf->fastcgi_values = prev->fastcgi_values;

    }

    if (clcf->lmt_excpt && clcf->handler == NULL

        && (conf->upstream.upstream || conf->fastcgi_lengths))

    {

        clcf->handler = ngx_http_fastcgi_handler;

    }

#if (NGX_PCRE)

    if (conf->split_regex == NULL) {

        conf->split_regex = prev->split_regex;

        conf->split_name = prev->split_name;

    }

#endif

    if (conf->params_source == NULL) {

        conf->params = prev->params;

#if (NGX_HTTP_CACHE)

        conf->params_cache = prev->params_cache;

#endif

        conf->params_source = prev->params_source;

    }

    rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params, NULL);

    if (rc != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache) {

        rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params_cache,

                                          ngx_http_fastcgi_cache_headers);

        if (rc != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

#endif

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_init_params(ngx_conf_t *cf, ngx_http_fastcgi_loc_conf_t *conf,

    ngx_http_fastcgi_params_t *params, ngx_keyval_t *default_params)

{

    u_char                       *p;

    size_t                        size;

    uintptr_t                    *code;

    ngx_uint_t                    i, nsrc;

    ngx_array_t                   headers_names, params_merged;

    ngx_keyval_t                 *h;

    ngx_hash_key_t               *hk;

    ngx_hash_init_t               hash;

    ngx_http_upstream_param_t    *src, *s;

    ngx_http_script_compile_t     sc;

    ngx_http_script_copy_code_t  *copy;

    if (params->hash.buckets) {

        return NGX_OK;

    }

    if (conf->params_source == NULL && default_params == NULL) {

        params->hash.buckets = (void *) 1;

        return NGX_OK;

    }

    params->lengths = ngx_array_create(cf->pool, 64, 1);

    if (params->lengths == NULL) {

        return NGX_ERROR;

    }

    params->values = ngx_array_create(cf->pool, 512, 1);

    if (params->values == NULL) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (conf->params_source) {

        src = conf->params_source->elts;

        nsrc = conf->params_source->nelts;

    } else {

        src = NULL;

        nsrc = 0;

    }

    if (default_params) {

        if (ngx_array_init(&params_merged, cf->temp_pool, 4,

                           sizeof(ngx_http_upstream_param_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        for (i = 0; i < nsrc; i++) {

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = src[i];

        }

        h = default_params;

        while (h->key.len) {

            src = params_merged.elts;

            nsrc = params_merged.nelts;

            for (i = 0; i < nsrc; i++) {

                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {

                    goto next;

                }

            }

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            s->key = h->key;

            s->value = h->value;

            s->skip_empty = 1;

        next:

            h++;

        }

        src = params_merged.elts;

        nsrc = params_merged.nelts;

    }

    for (i = 0; i < nsrc; i++) {

        if (src[i].key.len > sizeof("HTTP_") - 1

            && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)

        {

            hk = ngx_array_push(&headers_names);

            if (hk == NULL) {

                return NGX_ERROR;

            }

            hk->key.len = src[i].key.len - 5;

            hk->key.data = src[i].key.data + 5;

            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);

            hk->value = (void *) 1;

            if (src[i].value.len == 0) {

                continue;

            }

        }

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].key.len;

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].skip_empty;

        size = (sizeof(ngx_http_script_copy_code_t)

                + src[i].key.len + sizeof(uintptr_t) - 1)

               & ~(sizeof(uintptr_t) - 1);

        copy = ngx_array_push_n(params->values, size);

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = ngx_http_script_copy_code;

        copy->len = src[i].key.len;

        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

        ngx_memcpy(p, src[i].key.data, src[i].key.len);

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &src[i].value;

        sc.flushes = &params->flushes;

        sc.lengths = &params->lengths;

        sc.values = &params->values;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_ERROR;

        }

        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

        code = ngx_array_push_n(params->values, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) NULL;

    params->number = headers_names.nelts;

    hash.hash = &params->hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = 512;

    hash.bucket_size = 64;

    hash.name = "fastcgi_params_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);

}

static ngx_int_t

‌ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                       *p;

    ngx_http_fastcgi_ctx_t       *f;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    f = ngx_http_fastcgi_split(r, flcf);

    if (f == NULL) {

        return NGX_ERROR;

    }

    if (f->script_name.len == 0

        || f->script_name.data[f->script_name.len - 1] != '/')

    {

        v->len = f->script_name.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = f->script_name.data;

        return NGX_OK;

    }

    v->len = f->script_name.len + flcf->index.len;

    v->data = ngx_pnalloc(r->pool, v->len);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    p = ngx_copy(v->data, f->script_name.data, f->script_name.len);

    ngx_memcpy(p, flcf->index.data, flcf->index.len);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_fastcgi_ctx_t       *f;

    ngx_http_fastcgi_loc_conf_t  *flcf;

    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

    f = ngx_http_fastcgi_split(r, flcf);

    if (f == NULL) {

        return NGX_ERROR;

    }

    v->len = f->path_info.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = f->path_info.data;

    return NGX_OK;

}

static ngx_http_fastcgi_ctx_t *

‌ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)

{

    ngx_http_fastcgi_ctx_t       *f;

#if (NGX_PCRE)

    ngx_int_t                     n;

    int                           captures[(1 + 2) * 3];

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    if (f == NULL) {

        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));

        if (f == NULL) {

            return NULL;

        }

        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);

    }

    if (f->script_name.len) {

        return f;

    }

    if (flcf->split_regex == NULL) {

        f->script_name = r->uri;

        return f;

    }

    n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);

    if (n >= 0) { /* match */

        f->script_name.len = captures[3] - captures[2];

        f->script_name.data = r->uri.data + captures[2];

        f->path_info.len = captures[5] - captures[4];

        f->path_info.data = r->uri.data + captures[4];

        return f;

    }

    if (n == NGX_REGEX_NO_MATCHED) {

        f->script_name = r->uri;

        return f;

    }

    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                  ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",

                  n, &r->uri, &flcf->split_name);

    return NULL;

#else

    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);

    if (f == NULL) {

        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));

        if (f == NULL) {

            return NULL;

        }

        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);

    }

    f->script_name = r->uri;

    return f;

#endif

}

static char *

‌ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_fastcgi_loc_conf_t *flcf = conf;

    ngx_url_t                   u;

    ngx_str_t                  *value, *url;

    ngx_uint_t                  n;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_script_compile_t   sc;

    if (flcf->upstream.upstream || flcf->fastcgi_lengths) {

        return "is duplicate";

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_fastcgi_handler;

    if (clcf->name.data[clcf->name.len - 1] == '/') {

        clcf->auto_redirect = 1;

    }

    value = cf->args->elts;

    url = &value[1];

    n = ngx_http_script_variables_count(url);

    if (n) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = url;

        sc.lengths = &flcf->fastcgi_lengths;

        sc.values = &flcf->fastcgi_values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.no_resolve = 1;

    flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);

    if (flcf->upstream.upstream == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

#if (NGX_PCRE)

    ngx_http_fastcgi_loc_conf_t *flcf = conf;

    ngx_str_t            *value;

    ngx_regex_compile_t   rc;

    u_char                errstr[NGX_MAX_CONF_ERRSTR];

    value = cf->args->elts;

    flcf->split_name = value[1];

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pattern = value[1];

    rc.pool = cf->pool;

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

    if (ngx_regex_compile(&rc) != NGX_OK) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);

        return NGX_CONF_ERROR;

    }

    if (rc.captures != 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "pattern \"%V\" must have 2 captures", &value[1]);

        return NGX_CONF_ERROR;

    }

    flcf->split_regex = rc.regex;

    return NGX_CONF_OK;

#else

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "\"%V\" requires PCRE library", &cmd->name);

    return NGX_CONF_ERROR;

#endif

}

static char *

‌ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_fastcgi_loc_conf_t *flcf = conf;

    ngx_str_t                  *value;

    ngx_http_script_compile_t   sc;

    if (flcf->upstream.store != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        flcf->upstream.store = 0;

        return NGX_CONF_OK;

    }

#if (NGX_HTTP_CACHE)

    if (flcf->upstream.cache > 0) {

        return "is incompatible with \"fastcgi_cache\"";

    }

#endif

    flcf->upstream.store = 1;

    if (ngx_strcmp(value[1].data, "on") == 0) {

        return NGX_CONF_OK;

    }

    /* include the terminating '\0' into script */

    value[1].len++;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = &value[1];

    sc.lengths = &flcf->upstream.store_lengths;

    sc.values = &flcf->upstream.store_values;

    sc.variables = ngx_http_script_variables_count(&value[1]);

    sc.complete_lengths = 1;

    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#if (NGX_HTTP_CACHE)

static char *

‌ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_fastcgi_loc_conf_t *flcf = conf;

    ngx_str_t                         *value;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (flcf->upstream.cache != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        flcf->upstream.cache = 0;

        return NGX_CONF_OK;

    }

    if (flcf->upstream.store > 0) {

        return "is incompatible with \"fastcgi_store\"";

    }

    flcf->upstream.cache = 1;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths != NULL) {

        flcf->upstream.cache_value = ngx_palloc(cf->pool,

                                             sizeof(ngx_http_complex_value_t));

        if (flcf->upstream.cache_value == NULL) {

            return NGX_CONF_ERROR;

        }

        *flcf->upstream.cache_value = cv;

        return NGX_CONF_OK;

    }

    flcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,

                                                      &ngx_http_fastcgi_module);

    if (flcf->upstream.cache_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_fastcgi_loc_conf_t *flcf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (flcf->cache_key.value.data) {

        return "is duplicate";

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &flcf->cache_key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

static char *

‌ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)

{

#if (NGX_FREEBSD)

    ssize_t *np = data;

    if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"fastcgi_send_lowat\" must be less than %d "

                           "(sysctl net.inet.tcp.sendspace)",

                           ngx_freebsd_net_inet_tcp_sendspace);

        return NGX_CONF_ERROR;

    }

#elif !(NGX_HAVE_SO_SNDLOWAT)

    ssize_t *np = data;

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"fastcgi_send_lowat\" is not supported, ignored");

    *np = 0;

#endif

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_flv_module.c - nginx-1.7.9

Global variables defined

ngx_flv_header

ngx_http_flv_commands

ngx_http_flv_module

ngx_http_flv_module_ctx

Functions defined

ngx_http_flv

ngx_http_flv_handler

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

‌static ngx_command_t  ngx_http_flv_commands[] = {

    { ngx_string("flv"),

      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

      ngx_http_flv,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static u_char  ngx_flv_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";

‌static ngx_http_module_t  ngx_http_flv_module_ctx = {

    NULL,                          /* preconfiguration */

    NULL,                          /* 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 */

};

‌ngx_module_t  ngx_http_flv_module = {

    NGX_MODULE_V1,

    &ngx_http_flv_module_ctx,      /* module context */

    ngx_http_flv_commands,         /* module directives */

    NGX_HTTP_MODULE,               /* module type */

    NULL,                          /* init master */

    NULL,                          /* init module */

    NULL,                          /* init process */

    NULL,                          /* init thread */

    NULL,                          /* exit thread */

    NULL,                          /* exit process */

    NULL,                          /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_flv_handler(ngx_http_request_t *r)

{

    u_char                    *last;

    off_t                      start, len;

    size_t                     root;

    ngx_int_t                  rc;

    ngx_uint_t                 level, i;

    ngx_str_t                  path, value;

    ngx_log_t                 *log;

    ngx_buf_t                 *b;

    ngx_chain_t                out[2];

    ngx_open_file_info_t       of;

    ngx_http_core_loc_conf_t  *clcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_HTTP_NOT_ALLOWED;

    }

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

        return NGX_DECLINED;

    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {

        return rc;

    }

    last = ngx_http_map_uri_to_path(r, &path, &root, 0);

    if (last == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    log = r->connection->log;

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,

                   "http flv filename: \"%V\"", &path);

    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;

    }

    if (!of.is_file) {

        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed", path.data);

        }

        return NGX_DECLINED;

    }

    r->root_tested = !r->error_page;

    start = 0;

    len = of.size;

    i = 1;

    if (r->args.len) {

        if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {

            start = ngx_atoof(value.data, value.len);

            if (start == NGX_ERROR || start >= len) {

                start = 0;

            }

            if (start) {

                len = sizeof(ngx_flv_header) - 1 + len - start;

                i = 0;

            }

        }

    }

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

    r->headers_out.status = NGX_HTTP_OK;

    r->headers_out.content_length_n = len;

    r->headers_out.last_modified_time = of.mtime;

    if (ngx_http_set_etag(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_http_set_content_type(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (i == 0) {

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

        if (b == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        b->pos = ngx_flv_header;

        b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;

        b->memory = 1;

        out[0].buf = b;

        out[0].next = &out[1];

    }

    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;

    }

    r->allow_ranges = 1;

    rc = ngx_http_send_header(r);

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

        return rc;

    }

    b->file_pos = start;

    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[1].buf = b;

    out[1].next = NULL;

    return ngx_http_output_filter(r, &out[i]);

}

static char *

‌ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_flv_handler;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_geo_module.c - nginx-1.7.9

Global variables defined

ngx_http_geo_commands

ngx_http_geo_header

ngx_http_geo_module

ngx_http_geo_module_ctx

Data types defined

ngx_http_geo_conf_ctx_t

ngx_http_geo_ctx_t

ngx_http_geo_header_t

ngx_http_geo_high_ranges_t

ngx_http_geo_range_t

ngx_http_geo_trees_t

ngx_http_geo_variable_value_node_t

Functions defined

ngx_http_geo

ngx_http_geo_add_proxy

ngx_http_geo_add_range

ngx_http_geo_addr

ngx_http_geo_block

ngx_http_geo_cidr

ngx_http_geo_cidr_add

ngx_http_geo_cidr_value

ngx_http_geo_cidr_variable

ngx_http_geo_copy_values

ngx_http_geo_create_binary_base

ngx_http_geo_delete_range

ngx_http_geo_include

ngx_http_geo_include_binary_base

ngx_http_geo_range

ngx_http_geo_range_variable

ngx_http_geo_real_addr

ngx_http_geo_value

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_http_variable_value_t       *value;

    u_short                          start;

    u_short                          end;

‌} ngx_http_geo_range_t;

typedef struct {

    ngx_radix_tree_t                *tree;

#if (NGX_HAVE_INET6)

    ngx_radix_tree_t                *tree6;

#endif

‌} ngx_http_geo_trees_t;

typedef struct {

    ngx_http_geo_range_t           **low;

    ngx_http_variable_value_t       *default_value;

‌} ngx_http_geo_high_ranges_t;

typedef struct {

    ngx_str_node_t                   sn;

    ngx_http_variable_value_t       *value;

    size_t                           offset;

‌} ngx_http_geo_variable_value_node_t;

typedef struct {

    ngx_http_variable_value_t       *value;

    ngx_str_t                       *net;

    ngx_http_geo_high_ranges_t       high;

    ngx_radix_tree_t                *tree;

#if (NGX_HAVE_INET6)

    ngx_radix_tree_t                *tree6;

#endif

    ngx_rbtree_t                     rbtree;

    ngx_rbtree_node_t                sentinel;

    ngx_array_t                     *proxies;

    ngx_pool_t                      *pool;

    ngx_pool_t                      *temp_pool;

    size_t                           data_size;

    ngx_str_t                        include_name;

    ngx_uint_t                       includes;

    ngx_uint_t                       entries;

    unsigned                         ranges:1;

    unsigned                         outside_entries:1;

    unsigned                         allow_binary_include:1;

    unsigned                         binary_include:1;

    unsigned                         proxy_recursive:1;

‌} ngx_http_geo_conf_ctx_t;

typedef struct {

    union {

        ngx_http_geo_trees_t         trees;

        ngx_http_geo_high_ranges_t   high;

    } u;

    ngx_array_t                     *proxies;

    unsigned                         proxy_recursive:1;

    ngx_int_t                        index;

‌} ngx_http_geo_ctx_t;

static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,

    ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);

static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,

    ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);

static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);

static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *value);

static char *ngx_http_geo_add_range(ngx_conf_t *cf,

    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);

static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,

    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);

static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *value);

static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);

static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,

    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);

static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,

    ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);

static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,

    ngx_cidr_t *cidr);

static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *name);

static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,

    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);

static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);

static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

‌static ngx_command_t  ngx_http_geo_commands[] = {

    { ngx_string("geo"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,

      ngx_http_geo_block,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_geo_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* 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 */

};

‌ngx_module_t  ngx_http_geo_module = {

    NGX_MODULE_V1,

    &ngx_http_geo_module_ctx,              /* module context */

    ngx_http_geo_commands,                 /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

typedef struct {

    u_char    GEORNG[6];

    u_char    version;

    u_char    ptr_size;

    uint32_t  endianness;

    uint32_t  crc32;

‌} ngx_http_geo_header_t;

‌static ngx_http_geo_header_t  ngx_http_geo_header = {

    { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0

};

/* geo range is AF_INET only */

static ngx_int_t

‌ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

    in_addr_t                   inaddr;

    ngx_addr_t                  addr;

    struct sockaddr_in         *sin;

    ngx_http_variable_value_t  *vv;

#if (NGX_HAVE_INET6)

    u_char                     *p;

    struct in6_addr            *inaddr6;

#endif

    if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {

        vv = (ngx_http_variable_value_t *)

                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);

        goto done;

    }

    switch (addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

        p = inaddr6->s6_addr;

        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {

            inaddr = p[12] << 24;

            inaddr += p[13] << 16;

            inaddr += p[14] << 8;

            inaddr += p[15];

            vv = (ngx_http_variable_value_t *)

                      ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

        } else {

            vv = (ngx_http_variable_value_t *)

                      ngx_radix128tree_find(ctx->u.trees.tree6, p);

        }

        break;

#endif

    default: /* AF_INET */

        sin = (struct sockaddr_in *) addr.sockaddr;

        inaddr = ntohl(sin->sin_addr.s_addr);

        vv = (ngx_http_variable_value_t *)

                  ngx_radix32tree_find(ctx->u.trees.tree, inaddr);

        break;

    }

done:

    *v = *vv;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http geo: %v", v);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

    in_addr_t              inaddr;

    ngx_addr_t             addr;

    ngx_uint_t             n;

    struct sockaddr_in    *sin;

    ngx_http_geo_range_t  *range;

#if (NGX_HAVE_INET6)

    u_char                *p;

    struct in6_addr       *inaddr6;

#endif

    *v = *ctx->u.high.default_value;

    if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {

        switch (addr.sockaddr->sa_family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

            if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {

                p = inaddr6->s6_addr;

                inaddr = p[12] << 24;

                inaddr += p[13] << 16;

                inaddr += p[14] << 8;

                inaddr += p[15];

            } else {

                inaddr = INADDR_NONE;

            }

            break;

#endif

        default: /* AF_INET */

            sin = (struct sockaddr_in *) addr.sockaddr;

            inaddr = ntohl(sin->sin_addr.s_addr);

            break;

        }

    } else {

        inaddr = INADDR_NONE;

    }

    if (ctx->u.high.low) {

        range = ctx->u.high.low[inaddr >> 16];

        if (range) {

            n = inaddr & 0xffff;

            do {

                if (n >= (ngx_uint_t) range->start

                    && n <= (ngx_uint_t) range->end)

                {

                    *v = *range->value;

                    break;

                }

            } while ((++range)->value);

        }

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http geo: %v", v);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,

    ngx_addr_t *addr)

{

    ngx_array_t  *xfwd;

    if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {

        return NGX_ERROR;

    }

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && ctx->proxies != NULL) {

        (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,

                                           ctx->proxies, ctx->proxy_recursive);

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,

    ngx_addr_t *addr)

{

    ngx_http_variable_value_t  *v;

    if (ctx->index == -1) {

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http geo started: %V", &r->connection->addr_text);

        addr->sockaddr = r->connection->sockaddr;

        addr->socklen = r->connection->socklen;

        /* addr->name = r->connection->addr_text; */

        return NGX_OK;

    }

    v = ngx_http_get_flushed_variable(r, ctx->index);

    if (v == NULL || v->not_found) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http geo not found");

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http geo started: %v", v);

    if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {

        return NGX_OK;

    }

    return NGX_ERROR;

}

static char *

‌ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                     *rv;

    size_t                    len;

    ngx_str_t                *value, name;

    ngx_uint_t                i;

    ngx_conf_t                save;

    ngx_pool_t               *pool;

    ngx_array_t              *a;

    ngx_http_variable_t      *var;

    ngx_http_geo_ctx_t       *geo;

    ngx_http_geo_conf_ctx_t   ctx;

#if (NGX_HAVE_INET6)

    static struct in6_addr    zero;

#endif

    value = cf->args->elts;

    geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));

    if (geo == NULL) {

        return NGX_CONF_ERROR;

    }

    name = value[1];

    if (name.data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &name);

        return NGX_CONF_ERROR;

    }

    name.len--;

    name.data++;

    if (cf->args->nelts == 3) {

        geo->index = ngx_http_get_variable_index(cf, &name);

        if (geo->index == NGX_ERROR) {

            return NGX_CONF_ERROR;

        }

        name = value[2];

        if (name.data[0] != '$') {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid variable name \"%V\"", &name);

            return NGX_CONF_ERROR;

        }

        name.len--;

        name.data++;

    } else {

        geo->index = -1;

    }

    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);

    if (pool == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));

    ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);

    if (ctx.temp_pool == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);

    ctx.pool = cf->pool;

    ctx.data_size = sizeof(ngx_http_geo_header_t)

                  + sizeof(ngx_http_variable_value_t)

                  + 0x10000 * sizeof(ngx_http_geo_range_t *);

    ctx.allow_binary_include = 1;

    save = *cf;

    cf->pool = pool;

    cf->ctx = &ctx;

    cf->handler = ngx_http_geo;

    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    geo->proxies = ctx.proxies;

    geo->proxy_recursive = ctx.proxy_recursive;

    if (ctx.ranges) {

        if (ctx.high.low && !ctx.binary_include) {

            for (i = 0; i < 0x10000; i++) {

                a = (ngx_array_t *) ctx.high.low[i];

                if (a == NULL || a->nelts == 0) {

                    continue;

                }

                len = a->nelts * sizeof(ngx_http_geo_range_t);

                ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));

                if (ctx.high.low[i] == NULL) {

                    return NGX_CONF_ERROR;

                }

                ngx_memcpy(ctx.high.low[i], a->elts, len);

                ctx.high.low[i][a->nelts].value = NULL;

                ctx.data_size += len + sizeof(void *);

            }

            if (ctx.allow_binary_include

                && !ctx.outside_entries

                && ctx.entries > 100000

                && ctx.includes == 1)

            {

                ngx_http_geo_create_binary_base(&ctx);

            }

        }

        if (ctx.high.default_value == NULL) {

            ctx.high.default_value = &ngx_http_variable_null_value;

        }

        geo->u.high = ctx.high;

        var->get_handler = ngx_http_geo_range_variable;

        var->data = (uintptr_t) geo;

        ngx_destroy_pool(ctx.temp_pool);

        ngx_destroy_pool(pool);

    } else {

        if (ctx.tree == NULL) {

            ctx.tree = ngx_radix_tree_create(cf->pool, -1);

            if (ctx.tree == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        geo->u.trees.tree = ctx.tree;

#if (NGX_HAVE_INET6)

        if (ctx.tree6 == NULL) {

            ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);

            if (ctx.tree6 == NULL) {

                return NGX_CONF_ERROR;

            }

        }

        geo->u.trees.tree6 = ctx.tree6;

#endif

        var->get_handler = ngx_http_geo_cidr_variable;

        var->data = (uintptr_t) geo;

        ngx_destroy_pool(ctx.temp_pool);

        ngx_destroy_pool(pool);

        if (ngx_radix32tree_insert(ctx.tree, 0, 0,

                                   (uintptr_t) &ngx_http_variable_null_value)

            == NGX_ERROR)

        {

            return NGX_CONF_ERROR;

        }

        /* NGX_BUSY is okay (default was set explicitly) */

#if (NGX_HAVE_INET6)

        if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,

                                    (uintptr_t) &ngx_http_variable_null_value)

            == NGX_ERROR)

        {

            return NGX_CONF_ERROR;

        }

#endif

    }

    return rv;

}

static char *

‌ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)

{

    char                     *rv;

    ngx_str_t                *value;

    ngx_cidr_t                cidr;

    ngx_http_geo_conf_ctx_t  *ctx;

    ctx = cf->ctx;

    value = cf->args->elts;

    if (cf->args->nelts == 1) {

        if (ngx_strcmp(value[0].data, "ranges") == 0) {

            if (ctx->tree

#if (NGX_HAVE_INET6)

                || ctx->tree6

#endif

               )

            {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "the \"ranges\" directive must be "

                                   "the first directive inside \"geo\" block");

                goto failed;

            }

            ctx->ranges = 1;

            rv = NGX_CONF_OK;

            goto done;

        }

        else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {

            ctx->proxy_recursive = 1;

            rv = NGX_CONF_OK;

            goto done;

        }

    }

    if (cf->args->nelts != 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid number of the geo parameters");

        goto failed;

    }

    if (ngx_strcmp(value[0].data, "include") == 0) {

        rv = ngx_http_geo_include(cf, ctx, &value[1]);

        goto done;

    } else if (ngx_strcmp(value[0].data, "proxy") == 0) {

        if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {

            goto failed;

        }

        rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);

        goto done;

    }

    if (ctx->ranges) {

        rv = ngx_http_geo_range(cf, ctx, value);

    } else {

        rv = ngx_http_geo_cidr(cf, ctx, value);

    }

done:

    ngx_reset_pool(cf->pool);

    return rv;

failed:

    ngx_reset_pool(cf->pool);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *value)

{

    u_char      *p, *last;

    in_addr_t    start, end;

    ngx_str_t   *net;

    ngx_uint_t   del;

    if (ngx_strcmp(value[0].data, "default") == 0) {

        if (ctx->high.default_value) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                "duplicate default geo range value: \"%V\", old value: \"%v\"",

                &value[1], ctx->high.default_value);

        }

        ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);

        if (ctx->high.default_value == NULL) {

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    if (ctx->binary_include) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "binary geo range base \"%s\" cannot be mixed with usual entries",

            ctx->include_name.data);

        return NGX_CONF_ERROR;

    }

    if (ctx->high.low == NULL) {

        ctx->high.low = ngx_pcalloc(ctx->pool,

                                    0x10000 * sizeof(ngx_http_geo_range_t *));

        if (ctx->high.low == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    ctx->entries++;

    ctx->outside_entries = 1;

    if (ngx_strcmp(value[0].data, "delete") == 0) {

        net = &value[1];

        del = 1;

    } else {

        net = &value[0];

        del = 0;

    }

    last = net->data + net->len;

    p = ngx_strlchr(net->data, last, '-');

    if (p == NULL) {

        goto invalid;

    }

    start = ngx_inet_addr(net->data, p - net->data);

    if (start == INADDR_NONE) {

        goto invalid;

    }

    start = ntohl(start);

    p++;

    end = ngx_inet_addr(p, last - p);

    if (end == INADDR_NONE) {

        goto invalid;

    }

    end = ntohl(end);

    if (start > end) {

        goto invalid;

    }

    if (del) {

        if (ngx_http_geo_delete_range(cf, ctx, start, end)) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "no address range \"%V\" to delete", net);

        }

        return NGX_CONF_OK;

    }

    ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);

    if (ctx->value == NULL) {

        return NGX_CONF_ERROR;

    }

    ctx->net = net;

    return ngx_http_geo_add_range(cf, ctx, start, end);

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);

    return NGX_CONF_ERROR;

}

/* the add procedure is optimized to add a growing up sequence */

static char *

‌ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    in_addr_t start, in_addr_t end)

{

    in_addr_t              n;

    ngx_uint_t             h, i, s, e;

    ngx_array_t           *a;

    ngx_http_geo_range_t  *range;

    for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {

        h = n >> 16;

        if (n == start) {

            s = n & 0xffff;

        } else {

            s = 0;

        }

        if ((n | 0xffff) > end) {

            e = end & 0xffff;

        } else {

            e = 0xffff;

        }

        a = (ngx_array_t *) ctx->high.low[h];

        if (a == NULL) {

            a = ngx_array_create(ctx->temp_pool, 64,

                                 sizeof(ngx_http_geo_range_t));

            if (a == NULL) {

                return NGX_CONF_ERROR;

            }

            ctx->high.low[h] = (ngx_http_geo_range_t *) a;

        }

        i = a->nelts;

        range = a->elts;

        while (i) {

            i--;

            if (e < (ngx_uint_t) range[i].start) {

                continue;

            }

            if (s > (ngx_uint_t) range[i].end) {

                /* add after the range */

                range = ngx_array_push(a);

                if (range == NULL) {

                    return NGX_CONF_ERROR;

                }

                range = a->elts;

                ngx_memmove(&range[i + 2], &range[i + 1],

                           (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));

                range[i + 1].start = (u_short) s;

                range[i + 1].end = (u_short) e;

                range[i + 1].value = ctx->value;

                goto next;

            }

            if (s == (ngx_uint_t) range[i].start

                && e == (ngx_uint_t) range[i].end)

            {

                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",

                    ctx->net, ctx->value, range[i].value);

                range[i].value = ctx->value;

                goto next;

            }

            if (s > (ngx_uint_t) range[i].start

                && e < (ngx_uint_t) range[i].end)

            {

                /* split the range and insert the new one */

                range = ngx_array_push(a);

                if (range == NULL) {

                    return NGX_CONF_ERROR;

                }

                range = ngx_array_push(a);

                if (range == NULL) {

                    return NGX_CONF_ERROR;

                }

                range = a->elts;

                ngx_memmove(&range[i + 3], &range[i + 1],

                           (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));

                range[i + 2].start = (u_short) (e + 1);

                range[i + 2].end = range[i].end;

                range[i + 2].value = range[i].value;

                range[i + 1].start = (u_short) s;

                range[i + 1].end = (u_short) e;

                range[i + 1].value = ctx->value;

                range[i].end = (u_short) (s - 1);

                goto next;

            }

            if (s == (ngx_uint_t) range[i].start

                && e < (ngx_uint_t) range[i].end)

            {

                /* shift the range start and insert the new range */

                range = ngx_array_push(a);

                if (range == NULL) {

                    return NGX_CONF_ERROR;

                }

                range = a->elts;

                ngx_memmove(&range[i + 1], &range[i],

                           (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

                range[i + 1].start = (u_short) (e + 1);

                range[i].start = (u_short) s;

                range[i].end = (u_short) e;

                range[i].value = ctx->value;

                goto next;

            }

            if (s > (ngx_uint_t) range[i].start

                && e == (ngx_uint_t) range[i].end)

            {

                /* shift the range end and insert the new range */

                range = ngx_array_push(a);

                if (range == NULL) {

                    return NGX_CONF_ERROR;

                }

                range = a->elts;

                ngx_memmove(&range[i + 2], &range[i + 1],

                           (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));

                range[i + 1].start = (u_short) s;

                range[i + 1].end = (u_short) e;

                range[i + 1].value = ctx->value;

                range[i].end = (u_short) (s - 1);

                goto next;

            }

            s = (ngx_uint_t) range[i].start;

            e = (ngx_uint_t) range[i].end;

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                         "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",

                         ctx->net,

                         h >> 8, h & 0xff, s >> 8, s & 0xff,

                         h >> 8, h & 0xff, e >> 8, e & 0xff);

            return NGX_CONF_ERROR;

        }

        /* add the first range */

        range = ngx_array_push(a);

        if (range == NULL) {

            return NGX_CONF_ERROR;

        }

        range->start = (u_short) s;

        range->end = (u_short) e;

        range->value = ctx->value;

    next:

        continue;

    }

    return NGX_CONF_OK;

}

static ngx_uint_t

‌ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    in_addr_t start, in_addr_t end)

{

    in_addr_t              n;

    ngx_uint_t             h, i, s, e, warn;

    ngx_array_t           *a;

    ngx_http_geo_range_t  *range;

    warn = 0;

    for (n = start; n <= end; n += 0x10000) {

        h = n >> 16;

        if (n == start) {

            s = n & 0xffff;

        } else {

            s = 0;

        }

        if ((n | 0xffff) > end) {

            e = end & 0xffff;

        } else {

            e = 0xffff;

        }

        a = (ngx_array_t *) ctx->high.low[h];

        if (a == NULL) {

            warn = 1;

            continue;

        }

        range = a->elts;

        for (i = 0; i < a->nelts; i++) {

            if (s == (ngx_uint_t) range[i].start

                && e == (ngx_uint_t) range[i].end)

            {

                ngx_memmove(&range[i], &range[i + 1],

                           (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));

                a->nelts--;

                break;

            }

            if (s != (ngx_uint_t) range[i].start

                && e != (ngx_uint_t) range[i].end)

            {

                continue;

            }

            warn = 1;

        }

    }

    return warn;

}

static char *

‌ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *value)

{

    char        *rv;

    ngx_int_t    rc, del;

    ngx_str_t   *net;

    ngx_cidr_t   cidr;

    if (ctx->tree == NULL) {

        ctx->tree = ngx_radix_tree_create(ctx->pool, -1);

        if (ctx->tree == NULL) {

            return NGX_CONF_ERROR;

        }

    }

#if (NGX_HAVE_INET6)

    if (ctx->tree6 == NULL) {

        ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);

        if (ctx->tree6 == NULL) {

            return NGX_CONF_ERROR;

        }

    }

#endif

    if (ngx_strcmp(value[0].data, "default") == 0) {

        cidr.family = AF_INET;

        cidr.u.in.addr = 0;

        cidr.u.in.mask = 0;

        rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

        if (rv != NGX_CONF_OK) {

            return rv;

        }

#if (NGX_HAVE_INET6)

        cidr.family = AF_INET6;

        ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

        rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

        if (rv != NGX_CONF_OK) {

            return rv;

        }

#endif

        return NGX_CONF_OK;

    }

    if (ngx_strcmp(value[0].data, "delete") == 0) {

        net = &value[1];

        del = 1;

    } else {

        net = &value[0];

        del = 0;

    }

    if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cidr.family == AF_INET) {

        cidr.u.in.addr = ntohl(cidr.u.in.addr);

        cidr.u.in.mask = ntohl(cidr.u.in.mask);

    }

    if (del) {

        switch (cidr.family) {

#if (NGX_HAVE_INET6)

        case AF_INET6:

            rc = ngx_radix128tree_delete(ctx->tree6,

                                         cidr.u.in6.addr.s6_addr,

                                         cidr.u.in6.mask.s6_addr);

            break;

#endif

        default: /* AF_INET */

            rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,

                                        cidr.u.in.mask);

            break;

        }

        if (rc != NGX_OK) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "no network \"%V\" to delete", net);

        }

        return NGX_CONF_OK;

    }

    return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);

}

static char *

‌ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)

{

    ngx_int_t                   rc;

    ngx_http_variable_value_t  *val, *old;

    val = ngx_http_geo_value(cf, ctx, value);

    if (val == NULL) {

        return NGX_CONF_ERROR;

    }

    switch (cidr->family) {

#if (NGX_HAVE_INET6)

    case AF_INET6:

        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,

                                     cidr->u.in6.mask.s6_addr,

                                     (uintptr_t) val);

        if (rc == NGX_OK) {

            return NGX_CONF_OK;

        }

        if (rc == NGX_ERROR) {

            return NGX_CONF_ERROR;

        }

        /* rc == NGX_BUSY */

        old = (ngx_http_variable_value_t *)

                   ngx_radix128tree_find(ctx->tree6,

                                         cidr->u.in6.addr.s6_addr);

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

              "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",

              net, val, old);

        rc = ngx_radix128tree_delete(ctx->tree6,

                                     cidr->u.in6.addr.s6_addr,

                                     cidr->u.in6.mask.s6_addr);

        if (rc == NGX_ERROR) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");

            return NGX_CONF_ERROR;

        }

        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,

                                     cidr->u.in6.mask.s6_addr,

                                     (uintptr_t) val);

        break;

#endif

    default: /* AF_INET */

        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,

                                    cidr->u.in.mask, (uintptr_t) val);

        if (rc == NGX_OK) {

            return NGX_CONF_OK;

        }

        if (rc == NGX_ERROR) {

            return NGX_CONF_ERROR;

        }

        /* rc == NGX_BUSY */

        old = (ngx_http_variable_value_t *)

                   ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

              "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",

              net, val, old);

        rc = ngx_radix32tree_delete(ctx->tree,

                                    cidr->u.in.addr, cidr->u.in.mask);

        if (rc == NGX_ERROR) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");

            return NGX_CONF_ERROR;

        }

        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,

                                    cidr->u.in.mask, (uintptr_t) val);

        break;

    }

    if (rc == NGX_OK) {

        return NGX_CONF_OK;

    }

    return NGX_CONF_ERROR;

}

static ngx_http_variable_value_t *

‌ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *value)

{

    uint32_t                             hash;

    ngx_http_variable_value_t           *val;

    ngx_http_geo_variable_value_node_t  *gvvn;

    hash = ngx_crc32_long(value->data, value->len);

    gvvn = (ngx_http_geo_variable_value_node_t *)

               ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);

    if (gvvn) {

        return gvvn->value;

    }

    val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));

    if (val == NULL) {

        return NULL;

    }

    val->len = value->len;

    val->data = ngx_pstrdup(ctx->pool, value);

    if (val->data == NULL) {

        return NULL;

    }

    val->valid = 1;

    val->no_cacheable = 0;

    val->not_found = 0;

    gvvn = ngx_palloc(ctx->temp_pool,

                      sizeof(ngx_http_geo_variable_value_node_t));

    if (gvvn == NULL) {

        return NULL;

    }

    gvvn->sn.node.key = hash;

    gvvn->sn.str.len = val->len;

    gvvn->sn.str.data = val->data;

    gvvn->value = val;

    gvvn->offset = 0;

    ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);

    ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,

                                sizeof(void *));

    return val;

}

static char *

‌ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_cidr_t *cidr)

{

    ngx_cidr_t  *c;

    if (ctx->proxies == NULL) {

        ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));

        if (ctx->proxies == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    c = ngx_array_push(ctx->proxies);

    if (c == NULL) {

        return NGX_CONF_ERROR;

    }

    *c = *cidr;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)

{

    ngx_int_t  rc;

    if (ngx_strcmp(net->data, "255.255.255.255") == 0) {

        cidr->family = AF_INET;

        cidr->u.in.addr = 0xffffffff;

        cidr->u.in.mask = 0xffffffff;

        return NGX_OK;

    }

    rc = ngx_ptocidr(net, cidr);

    if (rc == NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);

        return NGX_ERROR;

    }

    if (rc == NGX_DONE) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "low address bits of %V are meaningless", net);

    }

    return NGX_OK;

}

static char *

‌ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *name)

{

    char       *rv;

    ngx_str_t   file;

    file.len = name->len + 4;

    file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);

    if (file.data == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_sprintf(file.data, "%V.bin%Z", name);

    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (ctx->ranges) {

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

        switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {

        case NGX_OK:

            return NGX_CONF_OK;

        case NGX_ERROR:

            return NGX_CONF_ERROR;

        default:

            break;

        }

    }

    file.len -= 4;

    file.data[file.len] = '\0';

    ctx->include_name = file;

    if (ctx->outside_entries) {

        ctx->allow_binary_include = 0;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);

    rv = ngx_conf_parse(cf, &file);

    ctx->includes++;

    ctx->outside_entries = 0;

    return rv;

}

static ngx_int_t

‌ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,

    ngx_str_t *name)

{

    u_char                     *base, ch;

    time_t                      mtime;

    size_t                      size, len;

    ssize_t                     n;

    uint32_t                    crc32;

    ngx_err_t                   err;

    ngx_int_t                   rc;

    ngx_uint_t                  i;

    ngx_file_t                  file;

    ngx_file_info_t             fi;

    ngx_http_geo_range_t       *range, **ranges;

    ngx_http_geo_header_t      *header;

    ngx_http_variable_value_t  *vv;

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = *name;

    file.log = cf->log;

    file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);

    if (file.fd == NGX_INVALID_FILE) {

        err = ngx_errno;

        if (err != NGX_ENOENT) {

            ngx_conf_log_error(NGX_LOG_CRIT, cf, err,

                               ngx_open_file_n " \"%s\" failed", name->data);

        }

        return NGX_DECLINED;

    }

    if (ctx->outside_entries) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "binary geo range base \"%s\" cannot be mixed with usual entries",

            name->data);

        rc = NGX_ERROR;

        goto done;

    }

    if (ctx->binary_include) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "second binary geo range base \"%s\" cannot be mixed with \"%s\"",

            name->data, ctx->include_name.data);

        rc = NGX_ERROR;

        goto done;

    }

    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {

        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,

                           ngx_fd_info_n " \"%s\" failed", name->data);

        goto failed;

    }

    size = (size_t) ngx_file_size(&fi);

    mtime = ngx_file_mtime(&fi);

    ch = name->data[name->len - 4];

    name->data[name->len - 4] = '\0';

    if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {

        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,

                           ngx_file_info_n " \"%s\" failed", name->data);

        goto failed;

    }

    name->data[name->len - 4] = ch;

    if (mtime < ngx_file_mtime(&fi)) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "stale binary geo range base \"%s\"", name->data);

        goto failed;

    }

    base = ngx_palloc(ctx->pool, size);

    if (base == NULL) {

        goto failed;

    }

    n = ngx_read_file(&file, base, size, 0);

    if (n == NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,

                           ngx_read_file_n " \"%s\" failed", name->data);

        goto failed;

    }

    if ((size_t) n != size) {

        ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,

            ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",

            name->data, n, size);

        goto failed;

    }

    header = (ngx_http_geo_header_t *) base;

    if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

             "incompatible binary geo range base \"%s\"", name->data);

        goto failed;

    }

    ngx_crc32_init(crc32);

    vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));

    while (vv->data) {

        len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,

                        sizeof(void *));

        ngx_crc32_update(&crc32, (u_char *) vv, len);

        vv->data += (size_t) base;

        vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);

    }

    ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));

    vv++;

    ranges = (ngx_http_geo_range_t **) vv;

    for (i = 0; i < 0x10000; i++) {

        ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));

        if (ranges[i]) {

            ranges[i] = (ngx_http_geo_range_t *)

                            ((u_char *) ranges[i] + (size_t) base);

        }

    }

    range = (ngx_http_geo_range_t *) &ranges[0x10000];

    while ((u_char *) range < base + size) {

        while (range->value) {

            ngx_crc32_update(&crc32, (u_char *) range,

                             sizeof(ngx_http_geo_range_t));

            range->value = (ngx_http_variable_value_t *)

                               ((u_char *) range->value + (size_t) base);

            range++;

        }

        ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));

        range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));

    }

    ngx_crc32_final(crc32);

    if (crc32 != header->crc32) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                  "CRC32 mismatch in binary geo range base \"%s\"", name->data);

        goto failed;

    }

    ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,

                       "using binary geo range base \"%s\"", name->data);

    ctx->include_name = *name;

    ctx->binary_include = 1;

    ctx->high.low = ranges;

    rc = NGX_OK;

    goto done;

failed:

    rc = NGX_DECLINED;

done:

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,

                      ngx_close_file_n " \"%s\" failed", name->data);

    }

    return rc;

}

static void

‌ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)

{

    u_char                              *p;

    uint32_t                             hash;

    ngx_str_t                            s;

    ngx_uint_t                           i;

    ngx_file_mapping_t                   fm;

    ngx_http_geo_range_t                *r, *range, **ranges;

    ngx_http_geo_header_t               *header;

    ngx_http_geo_variable_value_node_t  *gvvn;

    fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);

    if (fm.name == NULL) {

        return;

    }

    ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);

    fm.size = ctx->data_size;

    fm.log = ctx->pool->log;

    ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,

                  "creating binary geo range base \"%s\"", fm.name);

    if (ngx_create_file_mapping(&fm) != NGX_OK) {

        return;

    }

    p = ngx_cpymem(fm.addr, &ngx_http_geo_header,

                   sizeof(ngx_http_geo_header_t));

    p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,

                                 ctx->rbtree.sentinel);

    p += sizeof(ngx_http_variable_value_t);

    ranges = (ngx_http_geo_range_t **) p;

    p += 0x10000 * sizeof(ngx_http_geo_range_t *);

    for (i = 0; i < 0x10000; i++) {

        r = ctx->high.low[i];

        if (r == NULL) {

            continue;

        }

        range = (ngx_http_geo_range_t *) p;

        ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);

        do {

            s.len = r->value->len;

            s.data = r->value->data;

            hash = ngx_crc32_long(s.data, s.len);

            gvvn = (ngx_http_geo_variable_value_node_t *)

                        ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);

            range->value = (ngx_http_variable_value_t *) gvvn->offset;

            range->start = r->start;

            range->end = r->end;

            range++;

        } while ((++r)->value);

        range->value = NULL;

        p = (u_char *) range + sizeof(void *);

    }

    header = fm.addr;

    header->crc32 = ngx_crc32_long((u_char *) fm.addr

                                       + sizeof(ngx_http_geo_header_t),

                                   fm.size - sizeof(ngx_http_geo_header_t));

    ngx_close_file_mapping(&fm);

}

static u_char *

‌ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel)

{

    ngx_http_variable_value_t           *vv;

    ngx_http_geo_variable_value_node_t  *gvvn;

    if (node == sentinel) {

        return p;

    }

    gvvn = (ngx_http_geo_variable_value_node_t *) node;

    gvvn->offset = p - base;

    vv = (ngx_http_variable_value_t *) p;

    *vv = *gvvn->value;

    p += sizeof(ngx_http_variable_value_t);

    vv->data = (u_char *) (p - base);

    p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);

    p = ngx_align_ptr(p, sizeof(void *));

    p = ngx_http_geo_copy_values(base, p, node->left, sentinel);

    return ngx_http_geo_copy_values(base, p, node->right, sentinel);

}

src/http/modules/ngx_http_geoip_module.c - nginx-1.7.9

Global variables defined

ngx_http_geoip_commands

ngx_http_geoip_country_functions

ngx_http_geoip_country_v6_functions

ngx_http_geoip_module

ngx_http_geoip_module_ctx

ngx_http_geoip_vars

Data types defined

ngx_http_geoip_conf_t

ngx_http_geoip_var_t

ngx_http_geoip_variable_handler_pt

ngx_http_geoip_variable_handler_v6_pt

Functions defined

ngx_http_geoip_add_variables

ngx_http_geoip_addr

ngx_http_geoip_addr_v6

ngx_http_geoip_cidr_value

ngx_http_geoip_city

ngx_http_geoip_city_float_variable

ngx_http_geoip_city_int_variable

ngx_http_geoip_city_variable

ngx_http_geoip_cleanup

ngx_http_geoip_country

ngx_http_geoip_country_variable

ngx_http_geoip_create_conf

ngx_http_geoip_get_city_record

ngx_http_geoip_init_conf

ngx_http_geoip_org

ngx_http_geoip_org_variable

ngx_http_geoip_proxy

ngx_http_geoip_region_name_variable

Macros defined

NGX_GEOIP_COUNTRY_CODE

NGX_GEOIP_COUNTRY_CODE3

NGX_GEOIP_COUNTRY_NAME

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <GeoIP.h>

#include <GeoIPCity.h>

‌#define NGX_GEOIP_COUNTRY_CODE   0

‌#define NGX_GEOIP_COUNTRY_CODE3  1

‌#define NGX_GEOIP_COUNTRY_NAME   2

typedef struct {

    GeoIP        *country;

    GeoIP        *org;

    GeoIP        *city;

    ngx_array_t  *proxies;    /* array of ngx_cidr_t */

    ngx_flag_t    proxy_recursive;

#if (NGX_HAVE_GEOIP_V6)

    unsigned      country_v6:1;

    unsigned      org_v6:1;

    unsigned      city_v6:1;

#endif

‌} ngx_http_geoip_conf_t;

typedef struct {

    ngx_str_t    *name;

    uintptr_t     data;

‌} ngx_http_geoip_var_t;

‌typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,

    u_long addr);

‌ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {

    GeoIP_country_code_by_ipnum,

    GeoIP_country_code3_by_ipnum,

    GeoIP_country_name_by_ipnum,

};

#if (NGX_HAVE_GEOIP_V6)

‌typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,

    geoipv6_t addr);

‌ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {

    GeoIP_country_code_by_ipnum_v6,

    GeoIP_country_code3_by_ipnum_v6,

    GeoIP_country_name_by_ipnum_v6,

};

#endif

static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);

static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);

static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);

static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);

static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,

    ngx_cidr_t *cidr);

static void ngx_http_geoip_cleanup(void *data);

‌static ngx_command_t  ngx_http_geoip_commands[] = {

    { ngx_string("geoip_country"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,

      ngx_http_geoip_country,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("geoip_org"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,

      ngx_http_geoip_org,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("geoip_city"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,

      ngx_http_geoip_city,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("geoip_proxy"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_http_geoip_proxy,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("geoip_proxy_recursive"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_geoip_conf_t, proxy_recursive),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_geoip_module_ctx = {

    ngx_http_geoip_add_variables,          /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_geoip_create_conf,            /* create main configuration */

    ngx_http_geoip_init_conf,              /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_geoip_module = {

    NGX_MODULE_V1,

    &ngx_http_geoip_module_ctx,            /* module context */

    ngx_http_geoip_commands,               /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_variable_t  ngx_http_geoip_vars[] = {

    { ngx_string("geoip_country_code"), NULL,

      ngx_http_geoip_country_variable,

      NGX_GEOIP_COUNTRY_CODE, 0, 0 },

    { ngx_string("geoip_country_code3"), NULL,

      ngx_http_geoip_country_variable,

      NGX_GEOIP_COUNTRY_CODE3, 0, 0 },

    { ngx_string("geoip_country_name"), NULL,

      ngx_http_geoip_country_variable,

      NGX_GEOIP_COUNTRY_NAME, 0, 0 },

    { ngx_string("geoip_org"), NULL,

      ngx_http_geoip_org_variable,

      0, 0, 0 },

    { ngx_string("geoip_city_continent_code"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, continent_code), 0, 0 },

    { ngx_string("geoip_city_country_code"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, country_code), 0, 0 },

    { ngx_string("geoip_city_country_code3"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, country_code3), 0, 0 },

    { ngx_string("geoip_city_country_name"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, country_name), 0, 0 },

    { ngx_string("geoip_region"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, region), 0, 0 },

    { ngx_string("geoip_region_name"), NULL,

      ngx_http_geoip_region_name_variable,

      0, 0, 0 },

    { ngx_string("geoip_city"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, city), 0, 0 },

    { ngx_string("geoip_postal_code"), NULL,

      ngx_http_geoip_city_variable,

      offsetof(GeoIPRecord, postal_code), 0, 0 },

    { ngx_string("geoip_latitude"), NULL,

      ngx_http_geoip_city_float_variable,

      offsetof(GeoIPRecord, latitude), 0, 0 },

    { ngx_string("geoip_longitude"), NULL,

      ngx_http_geoip_city_float_variable,

      offsetof(GeoIPRecord, longitude), 0, 0 },

    { ngx_string("geoip_dma_code"), NULL,

      ngx_http_geoip_city_int_variable,

      offsetof(GeoIPRecord, dma_code), 0, 0 },

    { ngx_string("geoip_area_code"), NULL,

      ngx_http_geoip_city_int_variable,

      offsetof(GeoIPRecord, area_code), 0, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

static u_long

‌ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)

{

    ngx_addr_t           addr;

    ngx_array_t         *xfwd;

    struct sockaddr_in  *sin;

    addr.sockaddr = r->connection->sockaddr;

    addr.socklen = r->connection->socklen;

    /* addr.name = r->connection->addr_text; */

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && gcf->proxies != NULL) {

        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,

                                           gcf->proxies, gcf->proxy_recursive);

    }

#if (NGX_HAVE_INET6)

    if (addr.sockaddr->sa_family == AF_INET6) {

        u_char           *p;

        in_addr_t         inaddr;

        struct in6_addr  *inaddr6;

        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {

            p = inaddr6->s6_addr;

            inaddr = p[12] << 24;

            inaddr += p[13] << 16;

            inaddr += p[14] << 8;

            inaddr += p[15];

            return inaddr;

        }

    }

#endif

    if (addr.sockaddr->sa_family != AF_INET) {

        return INADDR_NONE;

    }

    sin = (struct sockaddr_in *) addr.sockaddr;

    return ntohl(sin->sin_addr.s_addr);

}

#if (NGX_HAVE_GEOIP_V6)

static geoipv6_t

‌ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)

{

    ngx_addr_t            addr;

    ngx_array_t          *xfwd;

    in_addr_t             addr4;

    struct in6_addr       addr6;

    struct sockaddr_in   *sin;

    struct sockaddr_in6  *sin6;

    addr.sockaddr = r->connection->sockaddr;

    addr.socklen = r->connection->socklen;

    /* addr.name = r->connection->addr_text; */

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && gcf->proxies != NULL) {

        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,

                                           gcf->proxies, gcf->proxy_recursive);

    }

    switch (addr.sockaddr->sa_family) {

    case AF_INET:

        /* Produce IPv4-mapped IPv6 address. */

        sin = (struct sockaddr_in *) addr.sockaddr;

        addr4 = ntohl(sin->sin_addr.s_addr);

        ngx_memzero(&addr6, sizeof(struct in6_addr));

        addr6.s6_addr[10] = 0xff;

        addr6.s6_addr[11] = 0xff;

        addr6.s6_addr[12] = addr4 >> 24;

        addr6.s6_addr[13] = addr4 >> 16;

        addr6.s6_addr[14] = addr4 >> 8;

        addr6.s6_addr[15] = addr4;

        return addr6;

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) addr.sockaddr;

        return sin6->sin6_addr;

    default:

        return in6addr_any;

    }

}

#endif

static ngx_int_t

‌ngx_http_geoip_country_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_geoip_variable_handler_pt     handler =

        ngx_http_geoip_country_functions[data];

#if (NGX_HAVE_GEOIP_V6)

    ngx_http_geoip_variable_handler_v6_pt  handler_v6 =

        ngx_http_geoip_country_v6_functions[data];

#endif

    const char             *val;

    ngx_http_geoip_conf_t  *gcf;

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

    if (gcf->country == NULL) {

        goto not_found;

    }

#if (NGX_HAVE_GEOIP_V6)

    val = gcf->country_v6

              ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))

              : handler(gcf->country, ngx_http_geoip_addr(r, gcf));

#else

    val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));

#endif

    if (val == NULL) {

        goto not_found;

    }

    v->len = ngx_strlen(val);

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = (u_char *) val;

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geoip_org_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t                  len;

    char                   *val;

    ngx_http_geoip_conf_t  *gcf;

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

    if (gcf->org == NULL) {

        goto not_found;

    }

#if (NGX_HAVE_GEOIP_V6)

    val = gcf->org_v6

              ? GeoIP_name_by_ipnum_v6(gcf->org,

                                       ngx_http_geoip_addr_v6(r, gcf))

              : GeoIP_name_by_ipnum(gcf->org,

                                    ngx_http_geoip_addr(r, gcf));

#else

    val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));

#endif

    if (val == NULL) {

        goto not_found;

    }

    len = ngx_strlen(val);

    v->data = ngx_pnalloc(r->pool, len);

    if (v->data == NULL) {

        ngx_free(val);

        return NGX_ERROR;

    }

    ngx_memcpy(v->data, val, len);

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    ngx_free(val);

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geoip_city_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    char         *val;

    size_t        len;

    GeoIPRecord  *gr;

    gr = ngx_http_geoip_get_city_record(r);

    if (gr == NULL) {

        goto not_found;

    }

    val = *(char **) ((char *) gr + data);

    if (val == NULL) {

        goto no_value;

    }

    len = ngx_strlen(val);

    v->data = ngx_pnalloc(r->pool, len);

    if (v->data == NULL) {

        GeoIPRecord_delete(gr);

        return NGX_ERROR;

    }

    ngx_memcpy(v->data, val, len);

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    GeoIPRecord_delete(gr);

    return NGX_OK;

no_value:

    GeoIPRecord_delete(gr);

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geoip_region_name_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t        len;

    const char   *val;

    GeoIPRecord  *gr;

    gr = ngx_http_geoip_get_city_record(r);

    if (gr == NULL) {

        goto not_found;

    }

    val = GeoIP_region_name_by_code(gr->country_code, gr->region);

    GeoIPRecord_delete(gr);

    if (val == NULL) {

        goto not_found;

    }

    len = ngx_strlen(val);

    v->data = ngx_pnalloc(r->pool, len);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(v->data, val, len);

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geoip_city_float_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    float         val;

    GeoIPRecord  *gr;

    gr = ngx_http_geoip_get_city_record(r);

    if (gr == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);

    if (v->data == NULL) {

        GeoIPRecord_delete(gr);

        return NGX_ERROR;

    }

    val = *(float *) ((char *) gr + data);

    v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    GeoIPRecord_delete(gr);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_geoip_city_int_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    int           val;

    GeoIPRecord  *gr;

    gr = ngx_http_geoip_get_city_record(r);

    if (gr == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);

    if (v->data == NULL) {

        GeoIPRecord_delete(gr);

        return NGX_ERROR;

    }

    val = *(int *) ((char *) gr + data);

    v->len = ngx_sprintf(v->data, "%d", val) - v->data;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    GeoIPRecord_delete(gr);

    return NGX_OK;

}

static GeoIPRecord *

‌ngx_http_geoip_get_city_record(ngx_http_request_t *r)

{

    ngx_http_geoip_conf_t  *gcf;

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

    if (gcf->city) {

#if (NGX_HAVE_GEOIP_V6)

        return gcf->city_v6

                   ? GeoIP_record_by_ipnum_v6(gcf->city,

                                              ngx_http_geoip_addr_v6(r, gcf))

                   : GeoIP_record_by_ipnum(gcf->city,

                                           ngx_http_geoip_addr(r, gcf));

#else

        return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));

#endif

    }

    return NULL;

}

static ngx_int_t

‌ngx_http_geoip_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_geoip_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static void *

‌ngx_http_geoip_create_conf(ngx_conf_t *cf)

{

    ngx_pool_cleanup_t     *cln;

    ngx_http_geoip_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->proxy_recursive = NGX_CONF_UNSET;

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NULL;

    }

    cln->handler = ngx_http_geoip_cleanup;

    cln->data = conf;

    return conf;

}

static char *

‌ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)

{

    ngx_http_geoip_conf_t  *gcf = conf;

    ngx_conf_init_value(gcf->proxy_recursive, 0);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_geoip_conf_t  *gcf = conf;

    ngx_str_t  *value;

    if (gcf->country) {

        return "is duplicate";

    }

    value = cf->args->elts;

    gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

    if (gcf->country == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "GeoIP_open(\"%V\") failed", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (cf->args->nelts == 3) {

        if (ngx_strcmp(value[2].data, "utf8") == 0) {

            GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[2]);

            return NGX_CONF_ERROR;

        }

    }

    switch (gcf->country->databaseType) {

    case GEOIP_COUNTRY_EDITION:

        return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)

    case GEOIP_COUNTRY_EDITION_V6:

        gcf->country_v6 = 1;

        return NGX_CONF_OK;

#endif

    default:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid GeoIP database \"%V\" type:%d",

                           &value[1], gcf->country->databaseType);

        return NGX_CONF_ERROR;

    }

}

static char *

‌ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_geoip_conf_t  *gcf = conf;

    ngx_str_t  *value;

    if (gcf->org) {

        return "is duplicate";

    }

    value = cf->args->elts;

    gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

    if (gcf->org == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "GeoIP_open(\"%V\") failed", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (cf->args->nelts == 3) {

        if (ngx_strcmp(value[2].data, "utf8") == 0) {

            GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[2]);

            return NGX_CONF_ERROR;

        }

    }

    switch (gcf->org->databaseType) {

    case GEOIP_ISP_EDITION:

    case GEOIP_ORG_EDITION:

    case GEOIP_DOMAIN_EDITION:

    case GEOIP_ASNUM_EDITION:

        return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)

    case GEOIP_ISP_EDITION_V6:

    case GEOIP_ORG_EDITION_V6:

    case GEOIP_DOMAIN_EDITION_V6:

    case GEOIP_ASNUM_EDITION_V6:

        gcf->org_v6 = 1;

        return NGX_CONF_OK;

#endif

    default:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid GeoIP database \"%V\" type:%d",

                           &value[1], gcf->org->databaseType);

        return NGX_CONF_ERROR;

    }

}

static char *

‌ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_geoip_conf_t  *gcf = conf;

    ngx_str_t  *value;

    if (gcf->city) {

        return "is duplicate";

    }

    value = cf->args->elts;

    gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

    if (gcf->city == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "GeoIP_open(\"%V\") failed", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (cf->args->nelts == 3) {

        if (ngx_strcmp(value[2].data, "utf8") == 0) {

            GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[2]);

            return NGX_CONF_ERROR;

        }

    }

    switch (gcf->city->databaseType) {

    case GEOIP_CITY_EDITION_REV0:

    case GEOIP_CITY_EDITION_REV1:

        return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)

    case GEOIP_CITY_EDITION_REV0_V6:

    case GEOIP_CITY_EDITION_REV1_V6:

        gcf->city_v6 = 1;

        return NGX_CONF_OK;

#endif

    default:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid GeoIP City database \"%V\" type:%d",

                           &value[1], gcf->city->databaseType);

        return NGX_CONF_ERROR;

    }

}

static char *

‌ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_geoip_conf_t  *gcf = conf;

    ngx_str_t   *value;

    ngx_cidr_t  cidr, *c;

    value = cf->args->elts;

    if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (gcf->proxies == NULL) {

        gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));

        if (gcf->proxies == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    c = ngx_array_push(gcf->proxies);

    if (c == NULL) {

        return NGX_CONF_ERROR;

    }

    *c = cidr;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)

{

    ngx_int_t  rc;

    if (ngx_strcmp(net->data, "255.255.255.255") == 0) {

        cidr->family = AF_INET;

        cidr->u.in.addr = 0xffffffff;

        cidr->u.in.mask = 0xffffffff;

        return NGX_OK;

    }

    rc = ngx_ptocidr(net, cidr);

    if (rc == NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);

        return NGX_ERROR;

    }

    if (rc == NGX_DONE) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "low address bits of %V are meaningless", net);

    }

    return NGX_OK;

}

static void

‌ngx_http_geoip_cleanup(void *data)

{

    ngx_http_geoip_conf_t  *gcf = data;

    if (gcf->country) {

        GeoIP_delete(gcf->country);

    }

    if (gcf->org) {

        GeoIP_delete(gcf->org);

    }

    if (gcf->city) {

        GeoIP_delete(gcf->city);

    }

}

src/http/modules/ngx_http_gunzip_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_gunzip_filter_commands

ngx_http_gunzip_filter_module

ngx_http_gunzip_filter_module_ctx

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

ngx_http_gunzip_conf_t

ngx_http_gunzip_ctx_t

Functions defined

ngx_http_gunzip_body_filter

ngx_http_gunzip_create_conf

ngx_http_gunzip_filter_add_data

ngx_http_gunzip_filter_alloc

ngx_http_gunzip_filter_free

ngx_http_gunzip_filter_get_buf

ngx_http_gunzip_filter_inflate

ngx_http_gunzip_filter_inflate_end

ngx_http_gunzip_filter_inflate_start

ngx_http_gunzip_filter_init

ngx_http_gunzip_header_filter

ngx_http_gunzip_merge_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Maxim Dounin

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <zlib.h>

typedef struct {

    ngx_flag_t           enable;

    ngx_bufs_t           bufs;

‌} ngx_http_gunzip_conf_t;

typedef struct {

    ngx_chain_t         *in;

    ngx_chain_t         *free;

    ngx_chain_t         *busy;

    ngx_chain_t         *out;

    ngx_chain_t        **last_out;

    ngx_buf_t           *in_buf;

    ngx_buf_t           *out_buf;

    ngx_int_t            bufs;

    unsigned             started:1;

    unsigned             flush:4;

    unsigned             redo:1;

    unsigned             done:1;

    unsigned             nomem:1;

    z_stream             zstream;

    ngx_http_request_t  *request;

‌} ngx_http_gunzip_ctx_t;

static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx);

static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx);

static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx);

static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx);

static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx);

static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,

    u_int size);

static void ngx_http_gunzip_filter_free(void *opaque, void *address);

static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);

static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);

static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

‌static ngx_command_t  ngx_http_gunzip_filter_commands[] = {

    { ngx_string("gunzip"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gunzip_conf_t, enable),

      NULL },

    { ngx_string("gunzip_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gunzip_conf_t, bufs),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_gunzip_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_gunzip_filter_init,           /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_gunzip_create_conf,           /* create location configuration */

    ngx_http_gunzip_merge_conf             /* merge location configuration */

};

‌ngx_module_t  ngx_http_gunzip_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_gunzip_filter_module_ctx,    /* module context */

    ngx_http_gunzip_filter_commands,       /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_gunzip_header_filter(ngx_http_request_t *r)

{

    ngx_http_gunzip_ctx_t   *ctx;

    ngx_http_gunzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);

    /* TODO support multiple content-codings */

    /* TODO always gunzip - due to configuration or module request */

    /* TODO ignore content encoding? */

    if (!conf->enable

        || r->headers_out.content_encoding == NULL

        || r->headers_out.content_encoding->value.len != 4

        || ngx_strncasecmp(r->headers_out.content_encoding->value.data,

                           (u_char *) "gzip", 4) != 0)

    {

        return ngx_http_next_header_filter(r);

    }

    r->gzip_vary = 1;

    if (!r->gzip_tested) {

        if (ngx_http_gzip_ok(r) == NGX_OK) {

            return ngx_http_next_header_filter(r);

        }

    } else if (r->gzip_ok) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);

    ctx->request = r;

    r->filter_need_in_memory = 1;

    r->headers_out.content_encoding->hash = 0;

    r->headers_out.content_encoding = NULL;

    ngx_http_clear_content_length(r);

    ngx_http_clear_accept_ranges(r);

    ngx_http_weak_etag(r);

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    int                     rc;

    ngx_uint_t              flush;

    ngx_chain_t            *cl;

    ngx_http_gunzip_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);

    if (ctx == NULL || ctx->done) {

        return ngx_http_next_body_filter(r, in);

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http gunzip filter");

    if (!ctx->started) {

        if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {

            goto failed;

        }

    }

    if (in) {

        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {

            goto failed;

        }

    }

    if (ctx->nomem) {

        /* flush busy buffers */

        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {

            goto failed;

        }

        cl = NULL;

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,

                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);

        ctx->nomem = 0;

        flush = 0;

    } else {

        flush = ctx->busy ? 1 : 0;

    }

    for ( ;; ) {

        /* cycle while we can write to a client */

        for ( ;; ) {

            /* cycle while there is data to feed zlib and ... */

            rc = ngx_http_gunzip_filter_add_data(r, ctx);

            if (rc == NGX_DECLINED) {

                break;

            }

            if (rc == NGX_AGAIN) {

                continue;

            }

            /* ... there are buffers to write zlib output */

            rc = ngx_http_gunzip_filter_get_buf(r, ctx);

            if (rc == NGX_DECLINED) {

                break;

            }

            if (rc == NGX_ERROR) {

                goto failed;

            }

            rc = ngx_http_gunzip_filter_inflate(r, ctx);

            if (rc == NGX_OK) {

                break;

            }

            if (rc == NGX_ERROR) {

                goto failed;

            }

            /* rc == NGX_AGAIN */

        }

        if (ctx->out == NULL && !flush) {

            return ctx->busy ? NGX_AGAIN : NGX_OK;

        }

        rc = ngx_http_next_body_filter(r, ctx->out);

        if (rc == NGX_ERROR) {

            goto failed;

        }

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,

                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);

        ctx->last_out = &ctx->out;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "gunzip out: %p", ctx->out);

        ctx->nomem = 0;

        flush = 0;

        if (ctx->done) {

            return rc;

        }

    }

    /* unreachable */

failed:

    ctx->done = 1;

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx)

{

    int  rc;

    ctx->zstream.next_in = Z_NULL;

    ctx->zstream.avail_in = 0;

    ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;

    ctx->zstream.zfree = ngx_http_gunzip_filter_free;

    ctx->zstream.opaque = ctx;

    /* windowBits +16 to decode gzip, zlib 1.2.0.4+ */

    rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "inflateInit2() failed: %d", rc);

        return NGX_ERROR;

    }

    ctx->started = 1;

    ctx->last_out = &ctx->out;

    ctx->flush = Z_NO_FLUSH;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx)

{

    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gunzip in: %p", ctx->in);

    if (ctx->in == NULL) {

        return NGX_DECLINED;

    }

    ctx->in_buf = ctx->in->buf;

    ctx->in = ctx->in->next;

    ctx->zstream.next_in = ctx->in_buf->pos;

    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gunzip in_buf:%p ni:%p ai:%ud",

                   ctx->in_buf,

                   ctx->zstream.next_in, ctx->zstream.avail_in);

    if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {

        ctx->flush = Z_FINISH;

    } else if (ctx->in_buf->flush) {

        ctx->flush = Z_SYNC_FLUSH;

    } else if (ctx->zstream.avail_in == 0) {

        /* ctx->flush == Z_NO_FLUSH */

        return NGX_AGAIN;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx)

{

    ngx_http_gunzip_conf_t  *conf;

    if (ctx->zstream.avail_out) {

        return NGX_OK;

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);

    if (ctx->free) {

        ctx->out_buf = ctx->free->buf;

        ctx->free = ctx->free->next;

        ctx->out_buf->flush = 0;

    } else if (ctx->bufs < conf->bufs.num) {

        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);

        if (ctx->out_buf == NULL) {

            return NGX_ERROR;

        }

        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;

        ctx->out_buf->recycled = 1;

        ctx->bufs++;

    } else {

        ctx->nomem = 1;

        return NGX_DECLINED;

    }

    ctx->zstream.next_out = ctx->out_buf->pos;

    ctx->zstream.avail_out = conf->bufs.size;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx)

{

    int           rc;

    ngx_buf_t    *b;

    ngx_chain_t  *cl;

    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",

                   ctx->zstream.next_in, ctx->zstream.next_out,

                   ctx->zstream.avail_in, ctx->zstream.avail_out,

                   ctx->flush, ctx->redo);

    rc = inflate(&ctx->zstream, ctx->flush);

    if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "inflate() failed: %d, %d", ctx->flush, rc);

        return NGX_ERROR;

    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                   ctx->zstream.next_in, ctx->zstream.next_out,

                   ctx->zstream.avail_in, ctx->zstream.avail_out,

                   rc);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gunzip in_buf:%p pos:%p",

                   ctx->in_buf, ctx->in_buf->pos);

    if (ctx->zstream.next_in) {

        ctx->in_buf->pos = ctx->zstream.next_in;

        if (ctx->zstream.avail_in == 0) {

            ctx->zstream.next_in = NULL;

        }

    }

    ctx->out_buf->last = ctx->zstream.next_out;

    if (ctx->zstream.avail_out == 0) {

        /* zlib wants to output some more data */

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = ctx->out_buf;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        ctx->redo = 1;

        return NGX_AGAIN;

    }

    ctx->redo = 0;

    if (ctx->flush == Z_SYNC_FLUSH) {

        ctx->flush = Z_NO_FLUSH;

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        b = ctx->out_buf;

        if (ngx_buf_size(b) == 0) {

            b = ngx_calloc_buf(ctx->request->pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

        } else {

            ctx->zstream.avail_out = 0;

        }

        b->flush = 1;

        cl->buf = b;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        return NGX_OK;

    }

    if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) {

        if (rc != Z_STREAM_END) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "inflate() returned %d on response end", rc);

            return NGX_ERROR;

        }

        if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {

            return NGX_ERROR;

        }

        return NGX_OK;

    }

    if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {

        rc = inflateReset(&ctx->zstream);

        if (rc != Z_OK) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                          "inflateReset() failed: %d", rc);

            return NGX_ERROR;

        }

        ctx->redo = 1;

        return NGX_AGAIN;

    }

    if (ctx->in == NULL) {

        b = ctx->out_buf;

        if (ngx_buf_size(b) == 0) {

            return NGX_OK;

        }

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        ctx->zstream.avail_out = 0;

        cl->buf = b;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        return NGX_OK;

    }

    return NGX_AGAIN;

}

static ngx_int_t

‌ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,

    ngx_http_gunzip_ctx_t *ctx)

{

    int           rc;

    ngx_buf_t    *b;

    ngx_chain_t  *cl;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gunzip inflate end");

    rc = inflateEnd(&ctx->zstream);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "inflateEnd() failed: %d", rc);

        return NGX_ERROR;

    }

    b = ctx->out_buf;

    if (ngx_buf_size(b) == 0) {

        b = ngx_calloc_buf(ctx->request->pool);

        if (b == NULL) {

            return NGX_ERROR;

        }

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    cl->next = NULL;

    *ctx->last_out = cl;

    ctx->last_out = &cl->next;

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

    b->last_in_chain = 1;

    b->sync = 1;

    ctx->done = 1;

    return NGX_OK;

}

static void *

‌ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)

{

    ngx_http_gunzip_ctx_t *ctx = opaque;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,

                   "gunzip alloc: n:%ud s:%ud",

                   items, size);

    return ngx_palloc(ctx->request->pool, items * size);

}

static void

‌ngx_http_gunzip_filter_free(void *opaque, void *address)

{

#if 0

    ngx_http_gunzip_ctx_t *ctx = opaque;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,

                   "gunzip free: %p", address);

#endif

}

static void *

‌ngx_http_gunzip_create_conf(ngx_conf_t *cf)

{

    ngx_http_gunzip_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->bufs.num = 0;

     */

    conf->enable = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_gunzip_conf_t *prev = parent;

    ngx_http_gunzip_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,

                              (128 * 1024) / ngx_pagesize, ngx_pagesize);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_gunzip_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_gunzip_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_gunzip_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_gzip_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_gzip_comp_level_bounds

ngx_http_gzip_filter_commands

ngx_http_gzip_filter_module

ngx_http_gzip_filter_module_ctx

ngx_http_gzip_hash_p

ngx_http_gzip_ratio

ngx_http_gzip_window_p

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

gztrailer

gztrailer

ngx_http_gzip_conf_t

ngx_http_gzip_ctx_t

Functions defined

ngx_http_gzip_add_variables

ngx_http_gzip_body_filter

ngx_http_gzip_create_conf

ngx_http_gzip_filter_add_data

ngx_http_gzip_filter_alloc

ngx_http_gzip_filter_buffer

ngx_http_gzip_filter_deflate

ngx_http_gzip_filter_deflate_end

ngx_http_gzip_filter_deflate_start

ngx_http_gzip_filter_free

ngx_http_gzip_filter_free_copy_buf

ngx_http_gzip_filter_get_buf

ngx_http_gzip_filter_gzheader

ngx_http_gzip_filter_init

ngx_http_gzip_filter_memory

ngx_http_gzip_hash

ngx_http_gzip_header_filter

ngx_http_gzip_merge_conf

ngx_http_gzip_ratio_variable

ngx_http_gzip_window

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <zlib.h>

typedef struct {

    ngx_flag_t           enable;

    ngx_flag_t           no_buffer;

    ngx_hash_t           types;

    ngx_bufs_t           bufs;

    size_t               postpone_gzipping;

    ngx_int_t            level;

    size_t               wbits;

    size_t               memlevel;

    ssize_t              min_length;

    ngx_array_t         *types_keys;

‌} ngx_http_gzip_conf_t;

typedef struct {

    ngx_chain_t         *in;

    ngx_chain_t         *free;

    ngx_chain_t         *busy;

    ngx_chain_t         *out;

    ngx_chain_t        **last_out;

    ngx_chain_t         *copied;

    ngx_chain_t         *copy_buf;

    ngx_buf_t           *in_buf;

    ngx_buf_t           *out_buf;

    ngx_int_t            bufs;

    void                *preallocated;

    char                *free_mem;

    ngx_uint_t           allocated;

    int                  wbits;

    int                  memlevel;

    unsigned             flush:4;

    unsigned             redo:1;

    unsigned             done:1;

    unsigned             nomem:1;

    unsigned             gzheader:1;

    unsigned             buffering:1;

    size_t               zin;

    size_t               zout;

    uint32_t             crc32;

    z_stream             zstream;

    ngx_http_request_t  *request;

‌} ngx_http_gzip_ctx_t;

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

‌struct gztrailer {

    uint32_t  crc32;

    uint32_t  zlen;

};

#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */

‌struct gztrailer {

    u_char  crc32[4];

    u_char  zlen[4];

};

#endif

static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,

    ngx_chain_t *in);

static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,

    u_int size);

static void ngx_http_gzip_filter_free(void *opaque, void *address);

static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx);

static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);

static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);

static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);

static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);

static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);

‌static ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {

    ngx_conf_check_num_bounds, 1, 9

};

‌static ngx_conf_post_handler_pt  ngx_http_gzip_window_p = ngx_http_gzip_window;

‌static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;

‌static ngx_command_t  ngx_http_gzip_filter_commands[] = {

    { ngx_string("gzip"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, enable),

      NULL },

    { ngx_string("gzip_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, bufs),

      NULL },

    { ngx_string("gzip_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, types_keys),

      &ngx_http_html_default_types[0] },

    { ngx_string("gzip_comp_level"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, level),

      &ngx_http_gzip_comp_level_bounds },

    { ngx_string("gzip_window"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, wbits),

      &ngx_http_gzip_window_p },

    { ngx_string("gzip_hash"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, memlevel),

      &ngx_http_gzip_hash_p },

    { ngx_string("postpone_gzipping"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, postpone_gzipping),

      NULL },

    { ngx_string("gzip_no_buffer"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, no_buffer),

      NULL },

    { ngx_string("gzip_min_length"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_conf_t, min_length),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {

    ngx_http_gzip_add_variables,           /* preconfiguration */

    ngx_http_gzip_filter_init,             /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_gzip_create_conf,             /* create location configuration */

    ngx_http_gzip_merge_conf               /* merge location configuration */

};

‌ngx_module_t  ngx_http_gzip_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_gzip_filter_module_ctx,      /* module context */

    ngx_http_gzip_filter_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_gzip_header_filter(ngx_http_request_t *r)

{

    ngx_table_elt_t       *h;

    ngx_http_gzip_ctx_t   *ctx;

    ngx_http_gzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    if (!conf->enable

        || (r->headers_out.status != NGX_HTTP_OK

            && r->headers_out.status != NGX_HTTP_FORBIDDEN

            && r->headers_out.status != NGX_HTTP_NOT_FOUND)

        || (r->headers_out.content_encoding

            && r->headers_out.content_encoding->value.len)

        || (r->headers_out.content_length_n != -1

            && r->headers_out.content_length_n < conf->min_length)

        || ngx_http_test_content_type(r, &conf->types) == NULL

        || r->header_only)

    {

        return ngx_http_next_header_filter(r);

    }

    r->gzip_vary = 1;

#if (NGX_HTTP_DEGRADATION)

    {

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {

        return ngx_http_next_header_filter(r);

    }

    }

#endif

    if (!r->gzip_tested) {

        if (ngx_http_gzip_ok(r) != NGX_OK) {

            return ngx_http_next_header_filter(r);

        }

    } else if (!r->gzip_ok) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);

    ctx->request = r;

    ctx->buffering = (conf->postpone_gzipping != 0);

    ngx_http_gzip_filter_memory(r, ctx);

    h = ngx_list_push(&r->headers_out.headers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    h->hash = 1;

    ngx_str_set(&h->key, "Content-Encoding");

    ngx_str_set(&h->value, "gzip");

    r->headers_out.content_encoding = h;

    r->main_filter_need_in_memory = 1;

    ngx_http_clear_content_length(r);

    ngx_http_clear_accept_ranges(r);

    ngx_http_weak_etag(r);

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    int                   rc;

    ngx_uint_t            flush;

    ngx_chain_t          *cl;

    ngx_http_gzip_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);

    if (ctx == NULL || ctx->done || r->header_only) {

        return ngx_http_next_body_filter(r, in);

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http gzip filter");

    if (ctx->buffering) {

        /*

         * With default memory settings zlib starts to output gzipped data

         * only after it has got about 90K, so it makes sense to allocate

         * zlib memory (200-400K) only after we have enough data to compress.

         * Although we copy buffers, nevertheless for not big responses

         * this allows to allocate zlib memory, to compress and to output

         * the response in one step using hot CPU cache.

         */

        if (in) {

            switch (ngx_http_gzip_filter_buffer(ctx, in)) {

            case NGX_OK:

                return NGX_OK;

            case NGX_DONE:

                in = NULL;

                break;

            default:  /* NGX_ERROR */

                goto failed;

            }

        } else {

            ctx->buffering = 0;

        }

    }

    if (ctx->preallocated == NULL) {

        if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {

            goto failed;

        }

    }

    if (in) {

        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {

            goto failed;

        }

        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;

    }

    if (ctx->nomem) {

        /* flush busy buffers */

        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {

            goto failed;

        }

        cl = NULL;

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,

                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);

        ctx->nomem = 0;

        flush = 0;

    } else {

        flush = ctx->busy ? 1 : 0;

    }

    for ( ;; ) {

        /* cycle while we can write to a client */

        for ( ;; ) {

            /* cycle while there is data to feed zlib and ... */

            rc = ngx_http_gzip_filter_add_data(r, ctx);

            if (rc == NGX_DECLINED) {

                break;

            }

            if (rc == NGX_AGAIN) {

                continue;

            }

            /* ... there are buffers to write zlib output */

            rc = ngx_http_gzip_filter_get_buf(r, ctx);

            if (rc == NGX_DECLINED) {

                break;

            }

            if (rc == NGX_ERROR) {

                goto failed;

            }

            rc = ngx_http_gzip_filter_deflate(r, ctx);

            if (rc == NGX_OK) {

                break;

            }

            if (rc == NGX_ERROR) {

                goto failed;

            }

            /* rc == NGX_AGAIN */

        }

        if (ctx->out == NULL && !flush) {

            ngx_http_gzip_filter_free_copy_buf(r, ctx);

            return ctx->busy ? NGX_AGAIN : NGX_OK;

        }

        if (!ctx->gzheader) {

            if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {

                goto failed;

            }

        }

        rc = ngx_http_next_body_filter(r, ctx->out);

        if (rc == NGX_ERROR) {

            goto failed;

        }

        ngx_http_gzip_filter_free_copy_buf(r, ctx);

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,

                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);

        ctx->last_out = &ctx->out;

        ctx->nomem = 0;

        flush = 0;

        if (ctx->done) {

            return rc;

        }

    }

    /* unreachable */

failed:

    ctx->done = 1;

    if (ctx->preallocated) {

        deflateEnd(&ctx->zstream);

        ngx_pfree(r->pool, ctx->preallocated);

    }

    ngx_http_gzip_filter_free_copy_buf(r, ctx);

    return NGX_ERROR;

}

static void

‌ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)

{

    int                    wbits, memlevel;

    ngx_http_gzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    wbits = conf->wbits;

    memlevel = conf->memlevel;

    if (r->headers_out.content_length_n > 0) {

        /* the actual zlib window size is smaller by 262 bytes */

        while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {

            wbits--;

            memlevel--;

        }

        if (memlevel < 1) {

            memlevel = 1;

        }

    }

    ctx->wbits = wbits;

    ctx->memlevel = memlevel;

    /*

     * We preallocate a memory for zlib in one buffer (200K-400K), this

     * decreases a number of malloc() and free() calls and also probably

     * decreases a number of syscalls (sbrk()/mmap() and so on).

     * Besides we free the memory as soon as a gzipping will complete

     * and do not wait while a whole response will be sent to a client.

     *

     * 8K is for zlib deflate_state, it takes

     *  *) 5816 bytes on i386 and sparc64 (32-bit mode)

     *  *) 5920 bytes on amd64 and sparc64

     */

    ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));

}

static ngx_int_t

‌ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)

{

    size_t                 size, buffered;

    ngx_buf_t             *b, *buf;

    ngx_chain_t           *cl, **ll;

    ngx_http_request_t    *r;

    ngx_http_gzip_conf_t  *conf;

    r = ctx->request;

    r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;

    buffered = 0;

    ll = &ctx->in;

    for (cl = ctx->in; cl; cl = cl->next) {

        buffered += cl->buf->last - cl->buf->pos;

        ll = &cl->next;

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    while (in) {

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        b = in->buf;

        size = b->last - b->pos;

        buffered += size;

        if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {

            ctx->buffering = 0;

        }

        if (ctx->buffering && size) {

            buf = ngx_create_temp_buf(r->pool, size);

            if (buf == NULL) {

                return NGX_ERROR;

            }

            buf->last = ngx_cpymem(buf->pos, b->pos, size);

            b->pos = b->last;

            buf->last_buf = b->last_buf;

            buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;

            cl->buf = buf;

        } else {

            cl->buf = b;

        }

        *ll = cl;

        ll = &cl->next;

        in = in->next;

    }

    *ll = NULL;

    return ctx->buffering ? NGX_OK : NGX_DONE;

}

static ngx_int_t

‌ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx)

{

    int                    rc;

    ngx_http_gzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);

    if (ctx->preallocated == NULL) {

        return NGX_ERROR;

    }

    ctx->free_mem = ctx->preallocated;

    ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;

    ctx->zstream.zfree = ngx_http_gzip_filter_free;

    ctx->zstream.opaque = ctx;

    rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,

                      - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "deflateInit2() failed: %d", rc);

        return NGX_ERROR;

    }

    ctx->last_out = &ctx->out;

    ctx->crc32 = crc32(0L, Z_NULL, 0);

    ctx->flush = Z_NO_FLUSH;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)

{

    ngx_buf_t      *b;

    ngx_chain_t    *cl;

    static u_char  gzheader[10] =

                               { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };

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

    if (b == NULL) {

        return NGX_ERROR;

    }

    b->memory = 1;

    b->pos = gzheader;

    b->last = b->pos + 10;

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    cl->next = ctx->out;

    ctx->out = cl;

    ctx->gzheader = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)

{

    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {

        return NGX_OK;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gzip in: %p", ctx->in);

    if (ctx->in == NULL) {

        return NGX_DECLINED;

    }

    if (ctx->copy_buf) {

        /*

         * to avoid CPU cache trashing we do not free() just quit buf,

         * but postpone free()ing after zlib compressing and data output

         */

        ctx->copy_buf->next = ctx->copied;

        ctx->copied = ctx->copy_buf;

        ctx->copy_buf = NULL;

    }

    ctx->in_buf = ctx->in->buf;

    if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {

        ctx->copy_buf = ctx->in;

    }

    ctx->in = ctx->in->next;

    ctx->zstream.next_in = ctx->in_buf->pos;

    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gzip in_buf:%p ni:%p ai:%ud",

                   ctx->in_buf,

                   ctx->zstream.next_in, ctx->zstream.avail_in);

    if (ctx->in_buf->last_buf) {

        ctx->flush = Z_FINISH;

    } else if (ctx->in_buf->flush) {

        ctx->flush = Z_SYNC_FLUSH;

    }

    if (ctx->zstream.avail_in) {

        ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,

                           ctx->zstream.avail_in);

    } else if (ctx->flush == Z_NO_FLUSH) {

        return NGX_AGAIN;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)

{

    ngx_http_gzip_conf_t  *conf;

    if (ctx->zstream.avail_out) {

        return NGX_OK;

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    if (ctx->free) {

        ctx->out_buf = ctx->free->buf;

        ctx->free = ctx->free->next;

    } else if (ctx->bufs < conf->bufs.num) {

        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);

        if (ctx->out_buf == NULL) {

            return NGX_ERROR;

        }

        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;

        ctx->out_buf->recycled = 1;

        ctx->bufs++;

    } else {

        ctx->nomem = 1;

        return NGX_DECLINED;

    }

    ctx->zstream.next_out = ctx->out_buf->pos;

    ctx->zstream.avail_out = conf->bufs.size;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)

{

    int                    rc;

    ngx_buf_t             *b;

    ngx_chain_t           *cl;

    ngx_http_gzip_conf_t  *conf;

    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",

                 ctx->zstream.next_in, ctx->zstream.next_out,

                 ctx->zstream.avail_in, ctx->zstream.avail_out,

                 ctx->flush, ctx->redo);

    rc = deflate(&ctx->zstream, ctx->flush);

    if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "deflate() failed: %d, %d", ctx->flush, rc);

        return NGX_ERROR;

    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                   ctx->zstream.next_in, ctx->zstream.next_out,

                   ctx->zstream.avail_in, ctx->zstream.avail_out,

                   rc);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "gzip in_buf:%p pos:%p",

                   ctx->in_buf, ctx->in_buf->pos);

    if (ctx->zstream.next_in) {

        ctx->in_buf->pos = ctx->zstream.next_in;

        if (ctx->zstream.avail_in == 0) {

            ctx->zstream.next_in = NULL;

        }

    }

    ctx->out_buf->last = ctx->zstream.next_out;

    if (ctx->zstream.avail_out == 0) {

        /* zlib wants to output some more gzipped data */

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = ctx->out_buf;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        ctx->redo = 1;

        return NGX_AGAIN;

    }

    ctx->redo = 0;

    if (ctx->flush == Z_SYNC_FLUSH) {

        ctx->flush = Z_NO_FLUSH;

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        b = ctx->out_buf;

        if (ngx_buf_size(b) == 0) {

            b = ngx_calloc_buf(ctx->request->pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

        } else {

            ctx->zstream.avail_out = 0;

        }

        b->flush = 1;

        cl->buf = b;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

        return NGX_OK;

    }

    if (rc == Z_STREAM_END) {

        if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {

            return NGX_ERROR;

        }

        return NGX_OK;

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

    if (conf->no_buffer && ctx->in == NULL) {

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = ctx->out_buf;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        return NGX_OK;

    }

    return NGX_AGAIN;

}

static ngx_int_t

‌ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx)

{

    int                rc;

    ngx_buf_t         *b;

    ngx_chain_t       *cl;

    struct gztrailer  *trailer;

    ctx->zin = ctx->zstream.total_in;

    ctx->zout = 10 + ctx->zstream.total_out + 8;

    rc = deflateEnd(&ctx->zstream);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      "deflateEnd() failed: %d", rc);

        return NGX_ERROR;

    }

    ngx_pfree(r->pool, ctx->preallocated);

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = ctx->out_buf;

    cl->next = NULL;

    *ctx->last_out = cl;

    ctx->last_out = &cl->next;

    if (ctx->zstream.avail_out >= 8) {

        trailer = (struct gztrailer *) ctx->out_buf->last;

        ctx->out_buf->last += 8;

        ctx->out_buf->last_buf = 1;

    } else {

        b = ngx_create_temp_buf(r->pool, 8);

        if (b == NULL) {

            return NGX_ERROR;

        }

        b->last_buf = 1;

        cl = ngx_alloc_chain_link(r->pool);

        if (cl == NULL) {

            return NGX_ERROR;

        }

        cl->buf = b;

        cl->next = NULL;

        *ctx->last_out = cl;

        ctx->last_out = &cl->next;

        trailer = (struct gztrailer *) b->pos;

        b->last += 8;

    }

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

    trailer->crc32 = ctx->crc32;

    trailer->zlen = ctx->zin;

#else

    trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);

    trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);

    trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);

    trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);

    trailer->zlen[0] = (u_char) (ctx->zin & 0xff);

    trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);

    trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);

    trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);

#endif

    ctx->zstream.avail_in = 0;

    ctx->zstream.avail_out = 0;

    ctx->done = 1;

    r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

    return NGX_OK;

}

static void *

‌ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)

{

    ngx_http_gzip_ctx_t *ctx = opaque;

    void        *p;

    ngx_uint_t   alloc;

    alloc = items * size;

    if (alloc % 512 != 0 && alloc < 8192) {

        /*

         * The zlib deflate_state allocation, it takes about 6K,

         * we allocate 8K.  Other allocations are divisible by 512.

         */

        alloc = 8192;

    }

    if (alloc <= ctx->allocated) {

        p = ctx->free_mem;

        ctx->free_mem += alloc;

        ctx->allocated -= alloc;

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,

                       "gzip alloc: n:%ud s:%ud a:%ud p:%p",

                       items, size, alloc, p);

        return p;

    }

    ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,

                  "gzip filter failed to use preallocated memory: %ud of %ud",

                  items * size, ctx->allocated);

    p = ngx_palloc(ctx->request->pool, items * size);

    return p;

}

static void

‌ngx_http_gzip_filter_free(void *opaque, void *address)

{

#if 0

    ngx_http_gzip_ctx_t *ctx = opaque;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,

                   "gzip free: %p", address);

#endif

}

static void

‌ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,

    ngx_http_gzip_ctx_t *ctx)

{

    ngx_chain_t  *cl;

    for (cl = ctx->copied; cl; cl = cl->next) {

        ngx_pfree(r->pool, cl->buf->start);

    }

    ctx->copied = NULL;

}

static ngx_int_t

‌ngx_http_gzip_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var;

    var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_gzip_ratio_variable;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_gzip_ratio_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_uint_t            zint, zfrac;

    ngx_http_gzip_ctx_t  *ctx;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);

    if (ctx == NULL || ctx->zout == 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    zint = (ngx_uint_t) (ctx->zin / ctx->zout);

    zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);

    if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {

        /* the rounding, e.g., 2.125 to 2.13 */

        zfrac++;

        if (zfrac > 99) {

            zint++;

            zfrac = 0;

        }

    }

    v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;

    return NGX_OK;

}

static void *

‌ngx_http_gzip_create_conf(ngx_conf_t *cf)

{

    ngx_http_gzip_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->bufs.num = 0;

     *     conf->types = { NULL };

     *     conf->types_keys = NULL;

     */

    conf->enable = NGX_CONF_UNSET;

    conf->no_buffer = NGX_CONF_UNSET;

    conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;

    conf->level = NGX_CONF_UNSET;

    conf->wbits = NGX_CONF_UNSET_SIZE;

    conf->memlevel = NGX_CONF_UNSET_SIZE;

    conf->min_length = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_gzip_conf_t *prev = parent;

    ngx_http_gzip_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);

    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,

                              (128 * 1024) / ngx_pagesize, ngx_pagesize);

    ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,

                              0);

    ngx_conf_merge_value(conf->level, prev->level, 1);

    ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);

    ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,

                              MAX_MEM_LEVEL - 1);

    ngx_conf_merge_value(conf->min_length, prev->min_length, 20);

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_html_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_gzip_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_gzip_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_gzip_body_filter;

    return NGX_OK;

}

static char *

‌ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)

{

    size_t *np = data;

    size_t  wbits, wsize;

    wbits = 15;

    for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {

        if (wsize == *np) {

            *np = wbits;

            return NGX_CONF_OK;

        }

        wbits--;

    }

    return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";

}

static char *

‌ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)

{

    size_t *np = data;

    size_t  memlevel, hsize;

    memlevel = 9;

    for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {

        if (hsize == *np) {

            *np = memlevel;

            return NGX_CONF_OK;

        }

        memlevel--;

    }

    return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";

}

src/http/modules/ngx_http_gzip_static_module.c - nginx-1.7.9

Global variables defined

ngx_http_gzip_static

ngx_http_gzip_static_commands

ngx_http_gzip_static_module

ngx_http_gzip_static_module_ctx

Data types defined

ngx_http_gzip_static_conf_t

Functions defined

ngx_http_gzip_static_create_conf

ngx_http_gzip_static_handler

ngx_http_gzip_static_init

ngx_http_gzip_static_merge_conf

Macros defined

NGX_HTTP_GZIP_STATIC_ALWAYS

NGX_HTTP_GZIP_STATIC_OFF

NGX_HTTP_GZIP_STATIC_ON

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_GZIP_STATIC_OFF     0

‌#define NGX_HTTP_GZIP_STATIC_ON      1

‌#define NGX_HTTP_GZIP_STATIC_ALWAYS  2

typedef struct {

    ngx_uint_t  enable;

‌} ngx_http_gzip_static_conf_t;

static ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);

static void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);

static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);

‌static ngx_conf_enum_t  ngx_http_gzip_static[] = {

    { ngx_string("off"), NGX_HTTP_GZIP_STATIC_OFF },

    { ngx_string("on"), NGX_HTTP_GZIP_STATIC_ON },

    { ngx_string("always"), NGX_HTTP_GZIP_STATIC_ALWAYS },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_http_gzip_static_commands[] = {

    { ngx_string("gzip_static"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_gzip_static_conf_t, enable),

      &ngx_http_gzip_static },

      ngx_null_command

};

‌ngx_http_module_t  ngx_http_gzip_static_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_gzip_static_init,             /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_gzip_static_create_conf,      /* create location configuration */

    ngx_http_gzip_static_merge_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_gzip_static_module = {

    NGX_MODULE_V1,

    &ngx_http_gzip_static_module_ctx,      /* module context */

    ngx_http_gzip_static_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_gzip_static_handler(ngx_http_request_t *r)

{

    u_char                       *p;

    size_t                        root;

    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_table_elt_t              *h;

    ngx_open_file_info_t          of;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_gzip_static_conf_t  *gzcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_DECLINED;

    }

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

        return NGX_DECLINED;

    }

    gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {

        return NGX_DECLINED;

    }

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {

        rc = ngx_http_gzip_ok(r);

    } else {

        /* always */

        rc = NGX_OK;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->gzip_vary && rc != NGX_OK) {

        return NGX_DECLINED;

    }

    log = r->connection->log;

    p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);

    if (p == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    *p++ = '.';

    *p++ = 'g';

    *p++ = 'z';

    *p = '\0';

    path.len = p - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,

                   "http filename: \"%s\"", path.data);

    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:

            return NGX_DECLINED;

        case NGX_EACCES:

#if (NGX_HAVE_OPENAT)

        case NGX_EMLINK:

        case NGX_ELOOP:

#endif

            level = NGX_LOG_ERR;

            break;

        default:

            level = NGX_LOG_CRIT;

            break;

        }

        ngx_log_error(level, log, of.err,

                      "%s \"%s\" failed", of.failed, path.data);

        return NGX_DECLINED;

    }

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {

        r->gzip_vary = 1;

        if (rc != NGX_OK) {

            return NGX_DECLINED;

        }

    }

    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");

        return NGX_DECLINED;

    }

#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

    r->root_tested = !r->error_page;

    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_etag(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_http_set_content_type(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    h = ngx_list_push(&r->headers_out.headers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    h->hash = 1;

    ngx_str_set(&h->key, "Content-Encoding");

    ngx_str_set(&h->value, "gzip");

    r->headers_out.content_encoding = h;

    /* 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);

}

static void *

‌ngx_http_gzip_static_create_conf(ngx_conf_t *cf)

{

    ngx_http_gzip_static_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->enable = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_gzip_static_conf_t *prev = parent;

    ngx_http_gzip_static_conf_t *conf = child;

    ngx_conf_merge_uint_value(conf->enable, prev->enable,

                              NGX_HTTP_GZIP_STATIC_OFF);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_gzip_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_gzip_static_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_headers_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_headers_filter_commands

ngx_http_headers_filter_module

ngx_http_headers_filter_module_ctx

ngx_http_next_header_filter

ngx_http_set_headers

Data types defined

ngx_http_expires_t

ngx_http_header_val_s

ngx_http_header_val_t

ngx_http_headers_conf_t

ngx_http_set_header_pt

ngx_http_set_header_t

Functions defined

ngx_http_add_cache_control

ngx_http_add_header

ngx_http_headers_add

ngx_http_headers_create_conf

ngx_http_headers_expires

ngx_http_headers_filter

ngx_http_headers_filter_init

ngx_http_headers_merge_conf

ngx_http_parse_expires

ngx_http_set_expires

ngx_http_set_last_modified

ngx_http_set_response_header

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌typedef struct ngx_http_header_val_s  ngx_http_header_val_t;

‌typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,

    ngx_http_header_val_t *hv, ngx_str_t *value);

typedef struct {

    ngx_str_t                  name;

    ngx_uint_t                 offset;

    ngx_http_set_header_pt     handler;

‌} ngx_http_set_header_t;

‌struct ngx_http_header_val_s {

    ngx_http_complex_value_t   value;

    ngx_str_t                  key;

    ngx_http_set_header_pt     handler;

    ngx_uint_t                 offset;

    ngx_uint_t                 always;  /* unsigned  always:1 */

};

typedef enum {

    NGX_HTTP_EXPIRES_OFF,

    NGX_HTTP_EXPIRES_EPOCH,

    NGX_HTTP_EXPIRES_MAX,

    NGX_HTTP_EXPIRES_ACCESS,

    NGX_HTTP_EXPIRES_MODIFIED,

    NGX_HTTP_EXPIRES_DAILY,

    NGX_HTTP_EXPIRES_UNSET

‌} ngx_http_expires_t;

typedef struct {

    ngx_http_expires_t         expires;

    time_t                     expires_time;

    ngx_http_complex_value_t  *expires_value;

    ngx_array_t               *headers;

‌} ngx_http_headers_conf_t;

static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,

    ngx_http_headers_conf_t *conf);

static ngx_int_t ngx_http_parse_expires(ngx_str_t *value,

    ngx_http_expires_t *expires, time_t *expires_time, char **err);

static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,

    ngx_http_header_val_t *hv, ngx_str_t *value);

static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,

    ngx_http_header_val_t *hv, ngx_str_t *value);

static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,

    ngx_http_header_val_t *hv, ngx_str_t *value);

static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,

    ngx_http_header_val_t *hv, ngx_str_t *value);

static void *ngx_http_headers_create_conf(ngx_conf_t *cf);

static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);

static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_http_set_header_t  ngx_http_set_headers[] = {

    { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },

    { ngx_string("Last-Modified"),

                 offsetof(ngx_http_headers_out_t, last_modified),

                 ngx_http_set_last_modified },

    { ngx_string("ETag"),

                 offsetof(ngx_http_headers_out_t, etag),

                 ngx_http_set_response_header },

    { ngx_null_string, 0, NULL }

};

‌static ngx_command_t  ngx_http_headers_filter_commands[] = {

    { ngx_string("expires"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE12,

      ngx_http_headers_expires,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL},

    { ngx_string("add_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_CONF_TAKE23,

      ngx_http_headers_add,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL},

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_headers_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_headers_filter_init,          /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_headers_create_conf,          /* create location configuration */

    ngx_http_headers_merge_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_headers_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_headers_filter_module_ctx,   /* module context */

    ngx_http_headers_filter_commands,      /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

static ngx_int_t

‌ngx_http_headers_filter(ngx_http_request_t *r)

{

    ngx_str_t                 value;

    ngx_uint_t                i, safe_status;

    ngx_http_header_val_t    *h;

    ngx_http_headers_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);

    if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)

        || r != r->main)

    {

        return ngx_http_next_header_filter(r);

    }

    switch (r->headers_out.status) {

    case NGX_HTTP_OK:

    case NGX_HTTP_CREATED:

    case NGX_HTTP_NO_CONTENT:

    case NGX_HTTP_PARTIAL_CONTENT:

    case NGX_HTTP_MOVED_PERMANENTLY:

    case NGX_HTTP_MOVED_TEMPORARILY:

    case NGX_HTTP_SEE_OTHER:

    case NGX_HTTP_NOT_MODIFIED:

    case NGX_HTTP_TEMPORARY_REDIRECT:

        safe_status = 1;

        break;

    default:

        safe_status = 0;

        break;

    }

    if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {

        if (ngx_http_set_expires(r, conf) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    if (conf->headers) {

        h = conf->headers->elts;

        for (i = 0; i < conf->headers->nelts; i++) {

            if (!safe_status && !h[i].always) {

                continue;

            }

            if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {

                return NGX_ERROR;

            }

            if (h[i].handler(r, &h[i], &value) != NGX_OK) {

                return NGX_ERROR;

            }

        }

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)

{

    char                *err;

    size_t               len;

    time_t               now, expires_time, max_age;

    ngx_str_t            value;

    ngx_int_t            rc;

    ngx_uint_t           i;

    ngx_table_elt_t     *e, *cc, **ccp;

    ngx_http_expires_t   expires;

    expires = conf->expires;

    expires_time = conf->expires_time;

    if (conf->expires_value != NULL) {

        if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {

            return NGX_ERROR;

        }

        rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);

        if (rc != NGX_OK) {

            return NGX_OK;

        }

        if (expires == NGX_HTTP_EXPIRES_OFF) {

            return NGX_OK;

        }

    }

    e = r->headers_out.expires;

    if (e == NULL) {

        e = ngx_list_push(&r->headers_out.headers);

        if (e == NULL) {

            return NGX_ERROR;

        }

        r->headers_out.expires = e;

        e->hash = 1;

        ngx_str_set(&e->key, "Expires");

    }

    len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");

    e->value.len = len - 1;

    ccp = r->headers_out.cache_control.elts;

    if (ccp == NULL) {

        if (ngx_array_init(&r->headers_out.cache_control, r->pool,

                           1, sizeof(ngx_table_elt_t *))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        ccp = ngx_array_push(&r->headers_out.cache_control);

        if (ccp == NULL) {

            return NGX_ERROR;

        }

        cc = ngx_list_push(&r->headers_out.headers);

        if (cc == NULL) {

            return NGX_ERROR;

        }

        cc->hash = 1;

        ngx_str_set(&cc->key, "Cache-Control");

        *ccp = cc;

    } else {

        for (i = 1; i < r->headers_out.cache_control.nelts; i++) {

            ccp[i]->hash = 0;

        }

        cc = ccp[0];

    }

    if (expires == NGX_HTTP_EXPIRES_EPOCH) {

        e->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";

        ngx_str_set(&cc->value, "no-cache");

        return NGX_OK;

    }

    if (expires == NGX_HTTP_EXPIRES_MAX) {

        e->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";

        /* 10 years */

        ngx_str_set(&cc->value, "max-age=315360000");

        return NGX_OK;

    }

    e->value.data = ngx_pnalloc(r->pool, len);

    if (e->value.data == NULL) {

        return NGX_ERROR;

    }

    if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {

        ngx_memcpy(e->value.data, ngx_cached_http_time.data,

                   ngx_cached_http_time.len + 1);

        ngx_str_set(&cc->value, "max-age=0");

        return NGX_OK;

    }

    now = ngx_time();

    if (expires == NGX_HTTP_EXPIRES_DAILY) {

        expires_time = ngx_next_time(expires_time);

        max_age = expires_time - now;

    } else if (expires == NGX_HTTP_EXPIRES_ACCESS

               || r->headers_out.last_modified_time == -1)

    {

        max_age = expires_time;

        expires_time += now;

    } else {

        expires_time += r->headers_out.last_modified_time;

        max_age = expires_time - now;

    }

    ngx_http_time(e->value.data, expires_time);

    if (conf->expires_time < 0 || max_age < 0) {

        ngx_str_set(&cc->value, "no-cache");

        return NGX_OK;

    }

    cc->value.data = ngx_pnalloc(r->pool,

                                 sizeof("max-age=") + NGX_TIME_T_LEN + 1);

    if (cc->value.data == NULL) {

        return NGX_ERROR;

    }

    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)

                    - cc->value.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,

    time_t *expires_time, char **err)

{

    ngx_uint_t  minus;

    if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {

        if (value->len == 5 && ngx_strncmp(value->data, "epoch", 5) == 0) {

            *expires = NGX_HTTP_EXPIRES_EPOCH;

            return NGX_OK;

        }

        if (value->len == 3 && ngx_strncmp(value->data, "max", 3) == 0) {

            *expires = NGX_HTTP_EXPIRES_MAX;

            return NGX_OK;

        }

        if (value->len == 3 && ngx_strncmp(value->data, "off", 3) == 0) {

            *expires = NGX_HTTP_EXPIRES_OFF;

            return NGX_OK;

        }

    }

    if (value->data[0] == '@') {

        value->data++;

        value->len--;

        minus = 0;

        if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {

            *err = "daily time cannot be used with \"modified\" parameter";

            return NGX_ERROR;

        }

        *expires = NGX_HTTP_EXPIRES_DAILY;

    } else if (value->data[0] == '+') {

        value->data++;

        value->len--;

        minus = 0;

    } else if (value->data[0] == '-') {

        value->data++;

        value->len--;

        minus = 1;

    } else {

        minus = 0;

    }

    *expires_time = ngx_parse_time(value, 1);

    if (*expires_time == (time_t) NGX_ERROR) {

        *err = "invalid value";

        return NGX_ERROR;

    }

    if (*expires == NGX_HTTP_EXPIRES_DAILY

        && *expires_time > 24 * 60 * 60)

    {

        *err = "daily time value must be less than 24 hours";

        return NGX_ERROR;

    }

    if (minus) {

        *expires_time = - *expires_time;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,

    ngx_str_t *value)

{

    ngx_table_elt_t  *h;

    if (value->len) {

        h = ngx_list_push(&r->headers_out.headers);

        if (h == NULL) {

            return NGX_ERROR;

        }

        h->hash = 1;

        h->key = hv->key;

        h->value = *value;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,

    ngx_str_t *value)

{

    ngx_table_elt_t  *cc, **ccp;

    if (value->len == 0) {

        return NGX_OK;

    }

    ccp = r->headers_out.cache_control.elts;

    if (ccp == NULL) {

        if (ngx_array_init(&r->headers_out.cache_control, r->pool,

                           1, sizeof(ngx_table_elt_t *))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    ccp = ngx_array_push(&r->headers_out.cache_control);

    if (ccp == NULL) {

        return NGX_ERROR;

    }

    cc = ngx_list_push(&r->headers_out.headers);

    if (cc == NULL) {

        return NGX_ERROR;

    }

    cc->hash = 1;

    ngx_str_set(&cc->key, "Cache-Control");

    cc->value = *value;

    *ccp = cc;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,

    ngx_str_t *value)

{

    if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {

        return NGX_ERROR;

    }

    r->headers_out.last_modified_time =

        (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,

    ngx_str_t *value)

{

    ngx_table_elt_t  *h, **old;

    old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);

    if (value->len == 0) {

        if (*old) {

            (*old)->hash = 0;

            *old = NULL;

        }

        return NGX_OK;

    }

    if (*old) {

        h = *old;

    } else {

        h = ngx_list_push(&r->headers_out.headers);

        if (h == NULL) {

            return NGX_ERROR;

        }

        *old = h;

    }

    h->hash = 1;

    h->key = hv->key;

    h->value = *value;

    return NGX_OK;

}

static void *

‌ngx_http_headers_create_conf(ngx_conf_t *cf)

{

    ngx_http_headers_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->headers = NULL;

     *     conf->expires_time = 0;

     *     conf->expires_value = NULL;

     */

    conf->expires = NGX_HTTP_EXPIRES_UNSET;

    return conf;

}

static char *

‌ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_headers_conf_t *prev = parent;

    ngx_http_headers_conf_t *conf = child;

    if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {

        conf->expires = prev->expires;

        conf->expires_time = prev->expires_time;

        conf->expires_value = prev->expires_value;

        if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {

            conf->expires = NGX_HTTP_EXPIRES_OFF;

        }

    }

    if (conf->headers == NULL) {

        conf->headers = prev->headers;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_headers_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_headers_filter;

    return NGX_OK;

}

static char *

‌ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_headers_conf_t *hcf = conf;

    char                              *err;

    ngx_str_t                         *value;

    ngx_int_t                          rc;

    ngx_uint_t                         n;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (cf->args->nelts == 2) {

        hcf->expires = NGX_HTTP_EXPIRES_ACCESS;

        n = 1;

    } else { /* cf->args->nelts == 3 */

        if (ngx_strcmp(value[1].data, "modified") != 0) {

            return "invalid value";

        }

        hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;

        n = 2;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[n];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths != NULL) {

        hcf->expires_value = ngx_palloc(cf->pool,

                                        sizeof(ngx_http_complex_value_t));

        if (hcf->expires_value == NULL) {

            return NGX_CONF_ERROR;

        }

        *hcf->expires_value = cv;

        return NGX_CONF_OK;

    }

    rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,

                                &err);

    if (rc != NGX_OK) {

        return err;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_headers_conf_t *hcf = conf;

    ngx_str_t                         *value;

    ngx_uint_t                         i;

    ngx_http_header_val_t             *hv;

    ngx_http_set_header_t             *set;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (hcf->headers == NULL) {

        hcf->headers = ngx_array_create(cf->pool, 1,

                                        sizeof(ngx_http_header_val_t));

        if (hcf->headers == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    hv = ngx_array_push(hcf->headers);

    if (hv == NULL) {

        return NGX_CONF_ERROR;

    }

    hv->key = value[1];

    hv->handler = ngx_http_add_header;

    hv->offset = 0;

    hv->always = 0;

    set = ngx_http_set_headers;

    for (i = 0; set[i].name.len; i++) {

        if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {

            continue;

        }

        hv->offset = set[i].offset;

        hv->handler = set[i].handler;

        break;

    }

    if (value[2].len == 0) {

        ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));

        return NGX_CONF_OK;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &hv->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cf->args->nelts == 3) {

        return NGX_CONF_OK;

    }

    if (ngx_strcmp(value[3].data, "always") != 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[3]);

        return NGX_CONF_ERROR;

    }

    hv->always = 1;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_image_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_image_filter_commands

ngx_http_image_filter_module

ngx_http_image_filter_module_ctx

ngx_http_image_types

ngx_http_next_body_filter

ngx_http_next_header_filter

Data types defined

ngx_http_image_filter_conf_t

ngx_http_image_filter_ctx_t

Functions defined

ngx_http_image_asis

ngx_http_image_body_filter

ngx_http_image_cleanup

ngx_http_image_filter

ngx_http_image_filter_create_conf

ngx_http_image_filter_get_value

ngx_http_image_filter_init

ngx_http_image_filter_jpeg_quality

ngx_http_image_filter_merge_conf

ngx_http_image_filter_sharpen

ngx_http_image_filter_value

ngx_http_image_header_filter

ngx_http_image_json

ngx_http_image_length

ngx_http_image_new

ngx_http_image_out

ngx_http_image_process

ngx_http_image_read

ngx_http_image_resize

ngx_http_image_send

ngx_http_image_size

ngx_http_image_source

ngx_http_image_test

Macros defined

NGX_HTTP_IMAGE_BUFFERED

NGX_HTTP_IMAGE_CROP

NGX_HTTP_IMAGE_DONE

NGX_HTTP_IMAGE_GIF

NGX_HTTP_IMAGE_JPEG

NGX_HTTP_IMAGE_NONE

NGX_HTTP_IMAGE_OFF

NGX_HTTP_IMAGE_PASS

NGX_HTTP_IMAGE_PNG

NGX_HTTP_IMAGE_PROCESS

NGX_HTTP_IMAGE_READ

NGX_HTTP_IMAGE_RESIZE

NGX_HTTP_IMAGE_ROTATE

NGX_HTTP_IMAGE_SIZE

NGX_HTTP_IMAGE_START

NGX_HTTP_IMAGE_TEST

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <gd.h>

‌#define NGX_HTTP_IMAGE_OFF       0

‌#define NGX_HTTP_IMAGE_TEST      1

‌#define NGX_HTTP_IMAGE_SIZE      2

‌#define NGX_HTTP_IMAGE_RESIZE    3

‌#define NGX_HTTP_IMAGE_CROP      4

‌#define NGX_HTTP_IMAGE_ROTATE    5

‌#define NGX_HTTP_IMAGE_START     0

‌#define NGX_HTTP_IMAGE_READ      1

‌#define NGX_HTTP_IMAGE_PROCESS   2

‌#define NGX_HTTP_IMAGE_PASS      3

‌#define NGX_HTTP_IMAGE_DONE      4

‌#define NGX_HTTP_IMAGE_NONE      0

‌#define NGX_HTTP_IMAGE_JPEG      1

‌#define NGX_HTTP_IMAGE_GIF       2

‌#define NGX_HTTP_IMAGE_PNG       3

‌#define NGX_HTTP_IMAGE_BUFFERED  0x08

typedef struct {

    ngx_uint_t                   filter;

    ngx_uint_t                   width;

    ngx_uint_t                   height;

    ngx_uint_t                   angle;

    ngx_uint_t                   jpeg_quality;

    ngx_uint_t                   sharpen;

    ngx_flag_t                   transparency;

    ngx_flag_t                   interlace;

    ngx_http_complex_value_t    *wcv;

    ngx_http_complex_value_t    *hcv;

    ngx_http_complex_value_t    *acv;

    ngx_http_complex_value_t    *jqcv;

    ngx_http_complex_value_t    *shcv;

    size_t                       buffer_size;

‌} ngx_http_image_filter_conf_t;

typedef struct {

    u_char                      *image;

    u_char                      *last;

    size_t                       length;

    ngx_uint_t                   width;

    ngx_uint_t                   height;

    ngx_uint_t                   max_width;

    ngx_uint_t                   max_height;

    ngx_uint_t                   angle;

    ngx_uint_t                   phase;

    ngx_uint_t                   type;

    ngx_uint_t                   force;

‌} ngx_http_image_filter_ctx_t;

static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);

static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);

static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);

static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);

static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx);

static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx);

static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);

static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx);

static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx);

static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,

    ngx_http_image_filter_ctx_t *ctx);

static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,

    int colors);

static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,

    gdImagePtr img, int *size);

static void ngx_http_image_cleanup(void *data);

static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,

    ngx_http_complex_value_t *cv, ngx_uint_t v);

static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);

static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);

static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,

    ngx_command_t *cmd, void *conf);

static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_image_filter_commands[] = {

    { ngx_string("image_filter"),

      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_http_image_filter,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("image_filter_jpeg_quality"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_image_filter_jpeg_quality,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("image_filter_sharpen"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_image_filter_sharpen,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("image_filter_transparency"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_image_filter_conf_t, transparency),

      NULL },

   { ngx_string("image_filter_interlace"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_image_filter_conf_t, interlace),

      NULL },

    { ngx_string("image_filter_buffer"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_image_filter_conf_t, buffer_size),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_image_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_image_filter_init,            /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_image_filter_create_conf,     /* create location configuration */

    ngx_http_image_filter_merge_conf       /* merge location configuration */

};

‌ngx_module_t  ngx_http_image_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_image_filter_module_ctx,     /* module context */

    ngx_http_image_filter_commands,        /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

‌static ngx_str_t  ngx_http_image_types[] = {

    ngx_string("image/jpeg"),

    ngx_string("image/gif"),

    ngx_string("image/png")

};

static ngx_int_t

‌ngx_http_image_header_filter(ngx_http_request_t *r)

{

    off_t                          len;

    ngx_http_image_filter_ctx_t   *ctx;

    ngx_http_image_filter_conf_t  *conf;

    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);

    if (ctx) {

        ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);

        return ngx_http_next_header_filter(r);

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

    if (conf->filter == NGX_HTTP_IMAGE_OFF) {

        return ngx_http_next_header_filter(r);

    }

    if (r->headers_out.content_type.len

            >= sizeof("multipart/x-mixed-replace") - 1

        && ngx_strncasecmp(r->headers_out.content_type.data,

                           (u_char *) "multipart/x-mixed-replace",

                           sizeof("multipart/x-mixed-replace") - 1)

           == 0)

    {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "image filter: multipart/x-mixed-replace response");

        return NGX_ERROR;

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);

    len = r->headers_out.content_length_n;

    if (len != -1 && len > (off_t) conf->buffer_size) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "image filter: too big response: %O", len);

        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;

    }

    if (len == -1) {

        ctx->length = conf->buffer_size;

    } else {

        ctx->length = (size_t) len;

    }

    if (r->headers_out.refresh) {

        r->headers_out.refresh->hash = 0;

    }

    r->main_filter_need_in_memory = 1;

    r->allow_ranges = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_int_t                      rc;

    ngx_str_t                     *ct;

    ngx_chain_t                    out;

    ngx_http_image_filter_ctx_t   *ctx;

    ngx_http_image_filter_conf_t  *conf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");

    if (in == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);

    if (ctx == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    switch (ctx->phase) {

    case NGX_HTTP_IMAGE_START:

        ctx->type = ngx_http_image_test(r, in);

        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

        if (ctx->type == NGX_HTTP_IMAGE_NONE) {

            if (conf->filter == NGX_HTTP_IMAGE_SIZE) {

                out.buf = ngx_http_image_json(r, NULL);

                if (out.buf) {

                    out.next = NULL;

                    ctx->phase = NGX_HTTP_IMAGE_DONE;

                    return ngx_http_image_send(r, ctx, &out);

                }

            }

            return ngx_http_filter_finalize_request(r,

                                              &ngx_http_image_filter_module,

                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);

        }

        /* override content type */

        ct = &ngx_http_image_types[ctx->type - 1];

        r->headers_out.content_type_len = ct->len;

        r->headers_out.content_type = *ct;

        r->headers_out.content_type_lowcase = NULL;

        if (conf->filter == NGX_HTTP_IMAGE_TEST) {

            ctx->phase = NGX_HTTP_IMAGE_PASS;

            return ngx_http_image_send(r, ctx, in);

        }

        ctx->phase = NGX_HTTP_IMAGE_READ;

        /* fall through */

    case NGX_HTTP_IMAGE_READ:

        rc = ngx_http_image_read(r, in);

        if (rc == NGX_AGAIN) {

            return NGX_OK;

        }

        if (rc == NGX_ERROR) {

            return ngx_http_filter_finalize_request(r,

                                              &ngx_http_image_filter_module,

                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);

        }

        /* fall through */

    case NGX_HTTP_IMAGE_PROCESS:

        out.buf = ngx_http_image_process(r);

        if (out.buf == NULL) {

            return ngx_http_filter_finalize_request(r,

                                              &ngx_http_image_filter_module,

                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);

        }

        out.next = NULL;

        ctx->phase = NGX_HTTP_IMAGE_PASS;

        return ngx_http_image_send(r, ctx, &out);

    case NGX_HTTP_IMAGE_PASS:

        return ngx_http_next_body_filter(r, in);

    default: /* NGX_HTTP_IMAGE_DONE */

        rc = ngx_http_next_body_filter(r, NULL);

        /* NGX_ERROR resets any pending data */

        return (rc == NGX_OK) ? NGX_ERROR : rc;

    }

}

static ngx_int_t

‌ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,

    ngx_chain_t *in)

{

    ngx_int_t  rc;

    rc = ngx_http_next_header_filter(r);

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

        return NGX_ERROR;

    }

    rc = ngx_http_next_body_filter(r, in);

    if (ctx->phase == NGX_HTTP_IMAGE_DONE) {

        /* NGX_ERROR resets any pending data */

        return (rc == NGX_OK) ? NGX_ERROR : rc;

    }

    return rc;

}

static ngx_uint_t

‌ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)

{

    u_char  *p;

    p = in->buf->pos;

    if (in->buf->last - p < 16) {

        return NGX_HTTP_IMAGE_NONE;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "image filter: \"%c%c\"", p[0], p[1]);

    if (p[0] == 0xff && p[1] == 0xd8) {

        /* JPEG */

        return NGX_HTTP_IMAGE_JPEG;

    } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'

               && p[5] == 'a')

    {

        if (p[4] == '9' || p[4] == '7') {

            /* GIF */

            return NGX_HTTP_IMAGE_GIF;

        }

    } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'

               && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)

    {

        /* PNG */

        return NGX_HTTP_IMAGE_PNG;

    }

    return NGX_HTTP_IMAGE_NONE;

}

static ngx_int_t

‌ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)

{

    u_char                       *p;

    size_t                        size, rest;

    ngx_buf_t                    *b;

    ngx_chain_t                  *cl;

    ngx_http_image_filter_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);

    if (ctx->image == NULL) {

        ctx->image = ngx_palloc(r->pool, ctx->length);

        if (ctx->image == NULL) {

            return NGX_ERROR;

        }

        ctx->last = ctx->image;

    }

    p = ctx->last;

    for (cl = in; cl; cl = cl->next) {

        b = cl->buf;

        size = b->last - b->pos;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "image buf: %uz", size);

        rest = ctx->image + ctx->length - p;

        if (size > rest) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "image filter: too big response");

            return NGX_ERROR;

        }

        p = ngx_cpymem(p, b->pos, size);

        b->pos += size;

        if (b->last_buf) {

            ctx->last = p;

            return NGX_OK;

        }

    }

    ctx->last = p;

    r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;

    return NGX_AGAIN;

}

static ngx_buf_t *

‌ngx_http_image_process(ngx_http_request_t *r)

{

    ngx_int_t                      rc;

    ngx_http_image_filter_ctx_t   *ctx;

    ngx_http_image_filter_conf_t  *conf;

    r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;

    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);

    rc = ngx_http_image_size(r, ctx);

    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

    if (conf->filter == NGX_HTTP_IMAGE_SIZE) {

        return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);

    }

    ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);

    if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {

        if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {

            return NULL;

        }

        return ngx_http_image_resize(r, ctx);

    }

    ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);

    if (ctx->max_width == 0) {

        return NULL;

    }

    ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,

                                                      conf->height);

    if (ctx->max_height == 0) {

        return NULL;

    }

    if (rc == NGX_OK

        && ctx->width <= ctx->max_width

        && ctx->height <= ctx->max_height

        && ctx->angle == 0

        && !ctx->force)

    {

        return ngx_http_image_asis(r, ctx);

    }

    return ngx_http_image_resize(r, ctx);

}

static ngx_buf_t *

‌ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)

{

    size_t      len;

    ngx_buf_t  *b;

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

    if (b == NULL) {

        return NULL;

    }

    b->memory = 1;

    b->last_buf = 1;

    ngx_http_clean_header(r);

    r->headers_out.status = NGX_HTTP_OK;

    r->headers_out.content_type_len = sizeof("application/json") - 1;

    ngx_str_set(&r->headers_out.content_type, "application/json");

    r->headers_out.content_type_lowcase = NULL;

    if (ctx == NULL) {

        b->pos = (u_char *) "{}" CRLF;

        b->last = b->pos + sizeof("{}" CRLF) - 1;

        ngx_http_image_length(r, b);

        return b;

    }

    len = sizeof("{ \"img\" : "

                 "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1

          + 2 * NGX_SIZE_T_LEN;

    b->pos = ngx_pnalloc(r->pool, len);

    if (b->pos == NULL) {

        return NULL;

    }

    b->last = ngx_sprintf(b->pos,

                          "{ \"img\" : "

                                       "{ \"width\": %uz,"

                                        " \"height\": %uz,"

                                        " \"type\": \"%s\" } }" CRLF,

                          ctx->width, ctx->height,

                          ngx_http_image_types[ctx->type - 1].data + 6);

    ngx_http_image_length(r, b);

    return b;

}

static ngx_buf_t *

‌ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)

{

    ngx_buf_t  *b;

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

    if (b == NULL) {

        return NULL;

    }

    b->pos = ctx->image;

    b->last = ctx->last;

    b->memory = 1;

    b->last_buf = 1;

    ngx_http_image_length(r, b);

    return b;

}

static void

‌ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)

{

    r->headers_out.content_length_n = b->last - b->pos;

    if (r->headers_out.content_length) {

        r->headers_out.content_length->hash = 0;

    }

    r->headers_out.content_length = NULL;

}

static ngx_int_t

‌ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)

{

    u_char      *p, *last;

    size_t       len, app;

    ngx_uint_t   width, height;

    p = ctx->image;

    switch (ctx->type) {

    case NGX_HTTP_IMAGE_JPEG:

        p += 2;

        last = ctx->image + ctx->length - 10;

        width = 0;

        height = 0;

        app = 0;

        while (p < last) {

            if (p[0] == 0xff && p[1] != 0xff) {

                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "JPEG: %02xd %02xd", p[0], p[1]);

                p++;

                if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3

                     || *p == 0xc9 || *p == 0xca || *p == 0xcb)

                    && (width == 0 || height == 0))

                {

                    width = p[6] * 256 + p[7];

                    height = p[4] * 256 + p[5];

                }

                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "JPEG: %02xd %02xd", p[1], p[2]);

                len = p[1] * 256 + p[2];

                if (*p >= 0xe1 && *p <= 0xef) {

                    /* application data, e.g., EXIF, Adobe XMP, etc. */

                    app += len;

                }

                p += len;

                continue;

            }

            p++;

        }

        if (width == 0 || height == 0) {

            return NGX_DECLINED;

        }

        if (ctx->length / 20 < app) {

            /* force conversion if application data consume more than 5% */

            ctx->force = 1;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "app data size: %uz", app);

        }

        break;

    case NGX_HTTP_IMAGE_GIF:

        if (ctx->length < 10) {

            return NGX_DECLINED;

        }

        width = p[7] * 256 + p[6];

        height = p[9] * 256 + p[8];

        break;

    case NGX_HTTP_IMAGE_PNG:

        if (ctx->length < 24) {

            return NGX_DECLINED;

        }

        width = p[18] * 256 + p[19];

        height = p[22] * 256 + p[23];

        break;

    default:

        return NGX_DECLINED;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "image size: %d x %d", width, height);

    ctx->width = width;

    ctx->height = height;

    return NGX_OK;

}

static ngx_buf_t *

‌ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)

{

    int                            sx, sy, dx, dy, ox, oy, ax, ay, size,

                                   colors, palette, transparent, sharpen,

                                   red, green, blue, t;

    u_char                        *out;

    ngx_buf_t                     *b;

    ngx_uint_t                     resize;

    gdImagePtr                     src, dst;

    ngx_pool_cleanup_t            *cln;

    ngx_http_image_filter_conf_t  *conf;

    src = ngx_http_image_source(r, ctx);

    if (src == NULL) {

        return NULL;

    }

    sx = gdImageSX(src);

    sy = gdImageSY(src);

    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

    if (!ctx->force

        && ctx->angle == 0

        && (ngx_uint_t) sx <= ctx->max_width

        && (ngx_uint_t) sy <= ctx->max_height)

    {

        gdImageDestroy(src);

        return ngx_http_image_asis(r, ctx);

    }

    colors = gdImageColorsTotal(src);

    if (colors && conf->transparency) {

        transparent = gdImageGetTransparent(src);

        if (transparent != -1) {

            palette = colors;

            red = gdImageRed(src, transparent);

            green = gdImageGreen(src, transparent);

            blue = gdImageBlue(src, transparent);

            goto transparent;

        }

    }

    palette = 0;

    transparent = -1;

    red = 0;

    green = 0;

    blue = 0;

transparent:

    gdImageColorTransparent(src, -1);

    dx = sx;

    dy = sy;

    if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {

        if ((ngx_uint_t) dx > ctx->max_width) {

            dy = dy * ctx->max_width / dx;

            dy = dy ? dy : 1;

            dx = ctx->max_width;

        }

        if ((ngx_uint_t) dy > ctx->max_height) {

            dx = dx * ctx->max_height / dy;

            dx = dx ? dx : 1;

            dy = ctx->max_height;

        }

        resize = 1;

    } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {

        resize = 0;

    } else { /* NGX_HTTP_IMAGE_CROP */

        resize = 0;

        if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {

            if ((ngx_uint_t) dx > ctx->max_width) {

                dy = dy * ctx->max_width / dx;

                dy = dy ? dy : 1;

                dx = ctx->max_width;

                resize = 1;

            }

        } else {

            if ((ngx_uint_t) dy > ctx->max_height) {

                dx = dx * ctx->max_height / dy;

                dx = dx ? dx : 1;

                dy = ctx->max_height;

                resize = 1;

            }

        }

    }

    if (resize) {

        dst = ngx_http_image_new(r, dx, dy, palette);

        if (dst == NULL) {

            gdImageDestroy(src);

            return NULL;

        }

        if (colors == 0) {

            gdImageSaveAlpha(dst, 1);

            gdImageAlphaBlending(dst, 0);

        }

        gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);

        if (colors) {

            gdImageTrueColorToPalette(dst, 1, 256);

        }

        gdImageDestroy(src);

    } else {

        dst = src;

    }

    if (ctx->angle) {

        src = dst;

        ax = (dx % 2 == 0) ? 1 : 0;

        ay = (dy % 2 == 0) ? 1 : 0;

        switch (ctx->angle) {

        case 90:

        case 270:

            dst = ngx_http_image_new(r, dy, dx, palette);

            if (dst == NULL) {

                gdImageDestroy(src);

                return NULL;

            }

            if (ctx->angle == 90) {

                ox = dy / 2 + ay;

                oy = dx / 2 - ax;

            } else {

                ox = dy / 2 - ay;

                oy = dx / 2 + ax;

            }

            gdImageCopyRotated(dst, src, ox, oy, 0, 0,

                               dx + ax, dy + ay, ctx->angle);

            gdImageDestroy(src);

            t = dx;

            dx = dy;

            dy = t;

            break;

        case 180:

            dst = ngx_http_image_new(r, dx, dy, palette);

            if (dst == NULL) {

                gdImageDestroy(src);

                return NULL;

            }

            gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,

                               dx + ax, dy + ay, ctx->angle);

            gdImageDestroy(src);

            break;

        }

    }

    if (conf->filter == NGX_HTTP_IMAGE_CROP) {

        src = dst;

        if ((ngx_uint_t) dx > ctx->max_width) {

            ox = dx - ctx->max_width;

        } else {

            ox = 0;

        }

        if ((ngx_uint_t) dy > ctx->max_height) {

            oy = dy - ctx->max_height;

        } else {

            oy = 0;

        }

        if (ox || oy) {

            dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);

            if (dst == NULL) {

                gdImageDestroy(src);

                return NULL;

            }

            ox /= 2;

            oy /= 2;

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "image crop: %d x %d @ %d x %d",

                           dx, dy, ox, oy);

            if (colors == 0) {

                gdImageSaveAlpha(dst, 1);

                gdImageAlphaBlending(dst, 0);

            }

            gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);

            if (colors) {

                gdImageTrueColorToPalette(dst, 1, 256);

            }

            gdImageDestroy(src);

        }

    }

    if (transparent != -1 && colors) {

        gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));

    }

    sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);

    if (sharpen > 0) {

        gdImageSharpen(dst, sharpen);

    }

    gdImageInterlace(dst, (int) conf->interlace);

    out = ngx_http_image_out(r, ctx->type, dst, &size);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "image: %d x %d %d", sx, sy, colors);

    gdImageDestroy(dst);

    ngx_pfree(r->pool, ctx->image);

    if (out == NULL) {

        return NULL;

    }

    cln = ngx_pool_cleanup_add(r->pool, 0);

    if (cln == NULL) {

        gdFree(out);

        return NULL;

    }

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

    if (b == NULL) {

        gdFree(out);

        return NULL;

    }

    cln->handler = ngx_http_image_cleanup;

    cln->data = out;

    b->pos = out;

    b->last = out + size;

    b->memory = 1;

    b->last_buf = 1;

    ngx_http_image_length(r, b);

    ngx_http_weak_etag(r);

    return b;

}

static gdImagePtr

‌ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)

{

    char        *failed;

    gdImagePtr   img;

    img = NULL;

    switch (ctx->type) {

    case NGX_HTTP_IMAGE_JPEG:

        img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);

        failed = "gdImageCreateFromJpegPtr() failed";

        break;

    case NGX_HTTP_IMAGE_GIF:

        img = gdImageCreateFromGifPtr(ctx->length, ctx->image);

        failed = "gdImageCreateFromGifPtr() failed";

        break;

    case NGX_HTTP_IMAGE_PNG:

        img = gdImageCreateFromPngPtr(ctx->length, ctx->image);

        failed = "gdImageCreateFromPngPtr() failed";

        break;

    default:

        failed = "unknown image type";

        break;

    }

    if (img == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);

    }

    return img;

}

static gdImagePtr

‌ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)

{

    gdImagePtr  img;

    if (colors == 0) {

        img = gdImageCreateTrueColor(w, h);

        if (img == NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "gdImageCreateTrueColor() failed");

            return NULL;

        }

    } else {

        img = gdImageCreate(w, h);

        if (img == NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "gdImageCreate() failed");

            return NULL;

        }

    }

    return img;

}

static u_char *

‌ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,

    int *size)

{

    char                          *failed;

    u_char                        *out;

    ngx_int_t                      jq;

    ngx_http_image_filter_conf_t  *conf;

    out = NULL;

    switch (type) {

    case NGX_HTTP_IMAGE_JPEG:

        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

        jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);

        if (jq <= 0) {

            return NULL;

        }

        out = gdImageJpegPtr(img, size, jq);

        failed = "gdImageJpegPtr() failed";

        break;

    case NGX_HTTP_IMAGE_GIF:

        out = gdImageGifPtr(img, size);

        failed = "gdImageGifPtr() failed";

        break;

    case NGX_HTTP_IMAGE_PNG:

        out = gdImagePngPtr(img, size);

        failed = "gdImagePngPtr() failed";

        break;

    default:

        failed = "unknown image type";

        break;

    }

    if (out == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);

    }

    return out;

}

static void

‌ngx_http_image_cleanup(void *data)

{

    gdFree(data);

}

static ngx_uint_t

‌ngx_http_image_filter_get_value(ngx_http_request_t *r,

    ngx_http_complex_value_t *cv, ngx_uint_t v)

{

    ngx_str_t  val;

    if (cv == NULL) {

        return v;

    }

    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {

        return 0;

    }

    return ngx_http_image_filter_value(&val);

}

static ngx_uint_t

‌ngx_http_image_filter_value(ngx_str_t *value)

{

    ngx_int_t  n;

    if (value->len == 1 && value->data[0] == '-') {

        return (ngx_uint_t) -1;

    }

    n = ngx_atoi(value->data, value->len);

    if (n > 0) {

        return (ngx_uint_t) n;

    }

    return 0;

}

static void *

‌ngx_http_image_filter_create_conf(ngx_conf_t *cf)

{

    ngx_http_image_filter_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->width = 0;

     *     conf->height = 0;

     *     conf->angle = 0;

     *     conf->wcv = NULL;

     *     conf->hcv = NULL;

     *     conf->acv = NULL;

     *     conf->jqcv = NULL;

     *     conf->shcv = NULL;

     */

    conf->filter = NGX_CONF_UNSET_UINT;

    conf->jpeg_quality = NGX_CONF_UNSET_UINT;

    conf->sharpen = NGX_CONF_UNSET_UINT;

    conf->transparency = NGX_CONF_UNSET;

    conf->interlace = NGX_CONF_UNSET;

    conf->buffer_size = NGX_CONF_UNSET_SIZE;

    return conf;

}

static char *

‌ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_image_filter_conf_t *prev = parent;

    ngx_http_image_filter_conf_t *conf = child;

    if (conf->filter == NGX_CONF_UNSET_UINT) {

        if (prev->filter == NGX_CONF_UNSET_UINT) {

            conf->filter = NGX_HTTP_IMAGE_OFF;

        } else {

            conf->filter = prev->filter;

            conf->width = prev->width;

            conf->height = prev->height;

            conf->angle = prev->angle;

            conf->wcv = prev->wcv;

            conf->hcv = prev->hcv;

            conf->acv = prev->acv;

        }

    }

    if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {

        /* 75 is libjpeg default quality */

        ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);

        if (conf->jqcv == NULL) {

            conf->jqcv = prev->jqcv;

        }

    }

    if (conf->sharpen == NGX_CONF_UNSET_UINT) {

        ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);

        if (conf->shcv == NULL) {

            conf->shcv = prev->shcv;

        }

    }

    ngx_conf_merge_value(conf->transparency, prev->transparency, 1);

    ngx_conf_merge_value(conf->interlace, prev->interlace, 0);

    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,

                              1 * 1024 * 1024);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_image_filter_conf_t *imcf = conf;

    ngx_str_t                         *value;

    ngx_int_t                          n;

    ngx_uint_t                         i;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    i = 1;

    if (cf->args->nelts == 2) {

        if (ngx_strcmp(value[i].data, "off") == 0) {

            imcf->filter = NGX_HTTP_IMAGE_OFF;

        } else if (ngx_strcmp(value[i].data, "test") == 0) {

            imcf->filter = NGX_HTTP_IMAGE_TEST;

        } else if (ngx_strcmp(value[i].data, "size") == 0) {

            imcf->filter = NGX_HTTP_IMAGE_SIZE;

        } else {

            goto failed;

        }

        return NGX_CONF_OK;

    } else if (cf->args->nelts == 3) {

        if (ngx_strcmp(value[i].data, "rotate") == 0) {

            if (imcf->filter != NGX_HTTP_IMAGE_RESIZE

                && imcf->filter != NGX_HTTP_IMAGE_CROP)

            {

                imcf->filter = NGX_HTTP_IMAGE_ROTATE;

            }

            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

            ccv.cf = cf;

            ccv.value = &value[++i];

            ccv.complex_value = &cv;

            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

            if (cv.lengths == NULL) {

                n = ngx_http_image_filter_value(&value[i]);

                if (n != 90 && n != 180 && n != 270) {

                    goto failed;

                }

                imcf->angle = (ngx_uint_t) n;

            } else {

                imcf->acv = ngx_palloc(cf->pool,

                                       sizeof(ngx_http_complex_value_t));

                if (imcf->acv == NULL) {

                    return NGX_CONF_ERROR;

                }

                *imcf->acv = cv;

            }

            return NGX_CONF_OK;

        } else {

            goto failed;

        }

    }

    if (ngx_strcmp(value[i].data, "resize") == 0) {

        imcf->filter = NGX_HTTP_IMAGE_RESIZE;

    } else if (ngx_strcmp(value[i].data, "crop") == 0) {

        imcf->filter = NGX_HTTP_IMAGE_CROP;

    } else {

        goto failed;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[++i];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths == NULL) {

        n = ngx_http_image_filter_value(&value[i]);

        if (n == 0) {

            goto failed;

        }

        imcf->width = (ngx_uint_t) n;

    } else {

        imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

        if (imcf->wcv == NULL) {

            return NGX_CONF_ERROR;

        }

        *imcf->wcv = cv;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[++i];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths == NULL) {

        n = ngx_http_image_filter_value(&value[i]);

        if (n == 0) {

            goto failed;

        }

        imcf->height = (ngx_uint_t) n;

    } else {

        imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

        if (imcf->hcv == NULL) {

            return NGX_CONF_ERROR;

        }

        *imcf->hcv = cv;

    }

    return NGX_CONF_OK;

failed:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",

                       &value[i]);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf)

{

    ngx_http_image_filter_conf_t *imcf = conf;

    ngx_str_t                         *value;

    ngx_int_t                          n;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths == NULL) {

        n = ngx_http_image_filter_value(&value[1]);

        if (n <= 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        imcf->jpeg_quality = (ngx_uint_t) n;

    } else {

        imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

        if (imcf->jqcv == NULL) {

            return NGX_CONF_ERROR;

        }

        *imcf->jqcv = cv;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf)

{

    ngx_http_image_filter_conf_t *imcf = conf;

    ngx_str_t                         *value;

    ngx_int_t                          n;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths == NULL) {

        n = ngx_http_image_filter_value(&value[1]);

        if (n < 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid value \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        imcf->sharpen = (ngx_uint_t) n;

    } else {

        imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));

        if (imcf->shcv == NULL) {

            return NGX_CONF_ERROR;

        }

        *imcf->shcv = cv;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_image_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_image_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_image_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_index_module.c - nginx-1.7.9

Global variables defined

ngx_http_index_commands

ngx_http_index_module

ngx_http_index_module_ctx

Data types defined

ngx_http_index_loc_conf_t

ngx_http_index_t

Functions defined

ngx_http_index_create_loc_conf

ngx_http_index_error

ngx_http_index_handler

ngx_http_index_init

ngx_http_index_merge_loc_conf

ngx_http_index_set_index

ngx_http_index_test_dir

Macros defined

NGX_HTTP_DEFAULT_INDEX

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_str_t                name;

    ngx_array_t             *lengths;

    ngx_array_t             *values;

‌} ngx_http_index_t;

typedef struct {

    ngx_array_t             *indices;    /* array of ngx_http_index_t */

    size_t                   max_index_len;

‌} ngx_http_index_loc_conf_t;

‌#define NGX_HTTP_DEFAULT_INDEX   "index.html"

static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);

static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,

    ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);

static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);

static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_index_commands[] = {

    { ngx_string("index"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_index_set_index,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_index_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_index_init,                   /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_index_create_loc_conf,        /* create location configuration */

    ngx_http_index_merge_loc_conf          /* merge location configuration */

};

‌ngx_module_t  ngx_http_index_module = {

    NGX_MODULE_V1,

    &ngx_http_index_module_ctx,            /* module context */

    ngx_http_index_commands,               /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

/*

* Try to open/test the first index file before the test of directory

* existence because valid requests should prevail over invalid ones.

* If open()/stat() of a file will fail then stat() of a directory

* should be faster because kernel may have already cached some data.

* Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.

* Unix has ENOTDIR error; however, it's less helpful than Win32's one:

* it only indicates that path points to a regular file, not a directory.

*/

static ngx_int_t

‌ngx_http_index_handler(ngx_http_request_t *r)

{

    u_char                       *p, *name;

    size_t                        len, root, reserve, allocated;

    ngx_int_t                     rc;

    ngx_str_t                     path, uri;

    ngx_uint_t                    i, dir_tested;

    ngx_http_index_t             *index;

    ngx_open_file_info_t          of;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t      e;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_index_loc_conf_t    *ilcf;

    ngx_http_script_len_code_pt   lcode;

    if (r->uri.data[r->uri.len - 1] != '/') {

        return NGX_DECLINED;

    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {

        return NGX_DECLINED;

    }

    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    allocated = 0;

    root = 0;

    dir_tested = 0;

    name = NULL;

    /* suppress MSVC warning */

    path.data = NULL;

    index = ilcf->indices->elts;

    for (i = 0; i < ilcf->indices->nelts; i++) {

        if (index[i].lengths == NULL) {

            if (index[i].name.data[0] == '/') {

                return ngx_http_internal_redirect(r, &index[i].name, &r->args);

            }

            reserve = ilcf->max_index_len;

            len = index[i].name.len;

        } else {

            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

            e.ip = index[i].lengths->elts;

            e.request = r;

            e.flushed = 1;

            /* 1 is for terminating '\0' as in static names */

            len = 1;

            while (*(uintptr_t *) e.ip) {

                lcode = *(ngx_http_script_len_code_pt *) e.ip;

                len += lcode(&e);

            }

            /* 16 bytes are preallocation */

            reserve = len + 16;

        }

        if (reserve > allocated) {

            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);

            if (name == NULL) {

                return NGX_ERROR;

            }

            allocated = path.data + path.len - name;

        }

        if (index[i].values == NULL) {

            /* index[i].name.len includes the terminating '\0' */

            ngx_memcpy(name, index[i].name.data, index[i].name.len);

            path.len = (name + index[i].name.len - 1) - path.data;

        } else {

            e.ip = index[i].values->elts;

            e.pos = name;

            while (*(uintptr_t *) e.ip) {

                code = *(ngx_http_script_code_pt *) e.ip;

                code((ngx_http_script_engine_t *) &e);

            }

            if (*name == '/') {

                uri.len = len - 1;

                uri.data = name;

                return ngx_http_internal_redirect(r, &uri, &r->args);

            }

            path.len = e.pos - path.data;

            *e.pos = '\0';

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "open index \"%V\"", &path);

        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.test_only = 1;

        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)

        {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,

                           "%s \"%s\" failed", of.failed, path.data);

            if (of.err == 0) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

#if (NGX_HAVE_OPENAT)

            if (of.err == NGX_EMLINK

                || of.err == NGX_ELOOP)

            {

                return NGX_HTTP_FORBIDDEN;

            }

#endif

            if (of.err == NGX_ENOTDIR

                || of.err == NGX_ENAMETOOLONG

                || of.err == NGX_EACCES)

            {

                return ngx_http_index_error(r, clcf, path.data, of.err);

            }

            if (!dir_tested) {

                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);

                if (rc != NGX_OK) {

                    return rc;

                }

                dir_tested = 1;

            }

            if (of.err == NGX_ENOENT) {

                continue;

            }

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,

                          "%s \"%s\" failed", of.failed, path.data);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        uri.len = r->uri.len + len - 1;

        if (!clcf->alias) {

            uri.data = path.data + root;

        } else {

            uri.data = ngx_pnalloc(r->pool, uri.len);

            if (uri.data == NULL) {

                return NGX_HTTP_INTERNAL_SERVER_ERROR;

            }

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

            ngx_memcpy(p, name, len - 1);

        }

        return ngx_http_internal_redirect(r, &uri, &r->args);

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,

    u_char *path, u_char *last)

{

    u_char                c;

    ngx_str_t             dir;

    ngx_open_file_info_t  of;

    c = *last;

    if (c != '/' || path == last) {

        /* "alias" without trailing slash */

        c = *(++last);

    }

    *last = '\0';

    dir.len = last - path;

    dir.data = path;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http index check dir: \"%V\"", &dir);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.test_dir = 1;

    of.test_only = 1;

    of.valid = clcf->open_file_cache_valid;

    of.errors = clcf->open_file_cache_errors;

    if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)

        != NGX_OK)

    {

        if (of.err) {

#if (NGX_HAVE_OPENAT)

            if (of.err == NGX_EMLINK

                || of.err == NGX_ELOOP)

            {

                return NGX_HTTP_FORBIDDEN;

            }

#endif

            if (of.err == NGX_ENOENT) {

                *last = c;

                return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);

            }

            if (of.err == NGX_EACCES) {

                *last = c;

                /*

                 * ngx_http_index_test_dir() is called after the first index

                 * file testing has returned an error distinct from NGX_EACCES.

                 * This means that directory searching is allowed.

                 */

                return NGX_OK;

            }

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,

                          "%s \"%s\" failed", of.failed, dir.data);

        }

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    *last = c;

    if (of.is_dir) {

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                  "\"%s\" is not a directory", dir.data);

    return NGX_HTTP_INTERNAL_SERVER_ERROR;

}

static ngx_int_t

‌ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,

    u_char *file, ngx_err_t err)

{

    if (err == NGX_EACCES) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,

                      "\"%s\" is forbidden", file);

        return NGX_HTTP_FORBIDDEN;

    }

    if (clcf->log_not_found) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,

                      "\"%s\" is not found", file);

    }

    return NGX_HTTP_NOT_FOUND;

}

static void *

‌ngx_http_index_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_index_loc_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->indices = NULL;

    conf->max_index_len = 0;

    return conf;

}

static char *

‌ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_index_loc_conf_t  *prev = parent;

    ngx_http_index_loc_conf_t  *conf = child;

    ngx_http_index_t  *index;

    if (conf->indices == NULL) {

        conf->indices = prev->indices;

        conf->max_index_len = prev->max_index_len;

    }

    if (conf->indices == NULL) {

        conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));

        if (conf->indices == NULL) {

            return NGX_CONF_ERROR;

        }

        index = ngx_array_push(conf->indices);

        if (index == NULL) {

            return NGX_CONF_ERROR;

        }

        index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);

        index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;

        index->lengths = NULL;

        index->values = NULL;

        conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);

        return NGX_CONF_OK;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_index_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_index_handler;

    return NGX_OK;

}

/* TODO: warn about duplicate indices */

static char *

‌ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_index_loc_conf_t *ilcf = conf;

    ngx_str_t                  *value;

    ngx_uint_t                  i, n;

    ngx_http_index_t           *index;

    ngx_http_script_compile_t   sc;

    if (ilcf->indices == NULL) {

        ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));

        if (ilcf->indices == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {

            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                               "only the last index in \"index\" directive "

                               "should be absolute");

        }

        if (value[i].len == 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "index \"%V\" in \"index\" directive is invalid",

                               &value[1]);

            return NGX_CONF_ERROR;

        }

        index = ngx_array_push(ilcf->indices);

        if (index == NULL) {

            return NGX_CONF_ERROR;

        }

        index->name.len = value[i].len;

        index->name.data = value[i].data;

        index->lengths = NULL;

        index->values = NULL;

        n = ngx_http_script_variables_count(&value[i]);

        if (n == 0) {

            if (ilcf->max_index_len < index->name.len) {

                ilcf->max_index_len = index->name.len;

            }

            if (index->name.data[0] == '/') {

                continue;

            }

            /* include the terminating '\0' to the length to use ngx_memcpy() */

            index->name.len++;

            continue;

        }

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &value[i];

        sc.lengths = &index->lengths;

        sc.values = &index->values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_log_module.c - nginx-1.7.9

Global variables defined

ngx_http_access_log

ngx_http_combined_fmt

ngx_http_log_commands

ngx_http_log_module

ngx_http_log_module_ctx

ngx_http_log_vars

Data types defined

ngx_http_log_buf_t

ngx_http_log_fmt_t

ngx_http_log_loc_conf_t

ngx_http_log_main_conf_t

ngx_http_log_op_getlen_pt

ngx_http_log_op_run_pt

ngx_http_log_op_s

ngx_http_log_op_t

ngx_http_log_script_t

ngx_http_log_t

ngx_http_log_var_t

Functions defined

ngx_http_log_body_bytes_sent

ngx_http_log_bytes_sent

ngx_http_log_compile_format

ngx_http_log_copy_long

ngx_http_log_copy_short

ngx_http_log_create_loc_conf

ngx_http_log_create_main_conf

ngx_http_log_escape

ngx_http_log_flush

ngx_http_log_flush_handler

ngx_http_log_gzip

ngx_http_log_gzip_alloc

ngx_http_log_gzip_free

ngx_http_log_handler

ngx_http_log_init

ngx_http_log_iso8601

ngx_http_log_merge_loc_conf

ngx_http_log_msec

ngx_http_log_open_file_cache

ngx_http_log_pipe

ngx_http_log_request_length

ngx_http_log_request_time

ngx_http_log_script_write

ngx_http_log_set_format

ngx_http_log_set_log

ngx_http_log_status

ngx_http_log_time

ngx_http_log_variable

ngx_http_log_variable_compile

ngx_http_log_variable_getlen

ngx_http_log_write

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#if (NGX_ZLIB)

#include <zlib.h>

#endif

‌typedef struct ngx_http_log_op_s  ngx_http_log_op_t;

‌typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

‌typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,

    uintptr_t data);

‌struct ngx_http_log_op_s {

    size_t                      len;

    ngx_http_log_op_getlen_pt   getlen;

    ngx_http_log_op_run_pt      run;

    uintptr_t                   data;

};

typedef struct {

    ngx_str_t                   name;

    ngx_array_t                *flushes;

    ngx_array_t                *ops;        /* array of ngx_http_log_op_t */

‌} ngx_http_log_fmt_t;

typedef struct {

    ngx_array_t                 formats;    /* array of ngx_http_log_fmt_t */

    ngx_uint_t                  combined_used; /* unsigned  combined_used:1 */

‌} ngx_http_log_main_conf_t;

typedef struct {

    u_char                     *start;

    u_char                     *pos;

    u_char                     *last;

    ngx_event_t                *event;

    ngx_msec_t                  flush;

    ngx_int_t                   gzip;

‌} ngx_http_log_buf_t;

typedef struct {

    ngx_array_t                *lengths;

    ngx_array_t                *values;

‌} ngx_http_log_script_t;

typedef struct {

    ngx_open_file_t            *file;

    ngx_http_log_script_t      *script;

    time_t                      disk_full_time;

    time_t                      error_log_time;

    ngx_syslog_peer_t          *syslog_peer;

    ngx_http_log_fmt_t         *format;

    ngx_http_complex_value_t   *filter;

‌} ngx_http_log_t;

typedef struct {

    ngx_array_t                *logs;       /* array of ngx_http_log_t */

    ngx_open_file_cache_t      *open_file_cache;

    time_t                      open_file_cache_valid;

    ngx_uint_t                  open_file_cache_min_uses;

    ngx_uint_t                  off;        /* unsigned  off:1 */

‌} ngx_http_log_loc_conf_t;

typedef struct {

    ngx_str_t                   name;

    size_t                      len;

    ngx_http_log_op_run_pt      run;

‌} ngx_http_log_var_t;

static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,

    u_char *buf, size_t len);

static ssize_t ngx_http_log_script_write(ngx_http_request_t *r,

    ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);

#if (NGX_ZLIB)

static ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,

    ngx_int_t level, ngx_log_t *log);

static void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size);

static void ngx_http_log_gzip_free(void *opaque, void *address);

#endif

static void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log);

static void ngx_http_log_flush_handler(ngx_event_t *ev);

static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r,

    u_char *buf, ngx_http_log_op_t *op);

static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,

    ngx_http_log_op_t *op, ngx_str_t *value);

static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,

    uintptr_t data);

static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op);

static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);

static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_log_compile_format(ngx_conf_t *cf,

    ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);

static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_log_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_log_commands[] = {

    { ngx_string("log_format"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,

      ngx_http_log_set_format,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("access_log"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                        |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,

      ngx_http_log_set_log,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("open_log_file_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_http_log_open_file_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_log_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_log_init,                     /* postconfiguration */

    ngx_http_log_create_main_conf,         /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_log_create_loc_conf,          /* create location configuration */

    ngx_http_log_merge_loc_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_log_module = {

    NGX_MODULE_V1,

    &ngx_http_log_module_ctx,              /* module context */

    ngx_http_log_commands,                 /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH);

‌static ngx_str_t  ngx_http_combined_fmt =

    ngx_string("$remote_addr - $remote_user [$time_local] "

               "\"$request\" $status $body_bytes_sent "

               "\"$http_referer\" \"$http_user_agent\"");

‌static ngx_http_log_var_t  ngx_http_log_vars[] = {

    { ngx_string("pipe"), 1, ngx_http_log_pipe },

    { ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1,

                          ngx_http_log_time },

    { ngx_string("time_iso8601"), sizeof("1970-09-28T12:00:00+06:00") - 1,

                          ngx_http_log_iso8601 },

    { ngx_string("msec"), NGX_TIME_T_LEN + 4, ngx_http_log_msec },

    { ngx_string("request_time"), NGX_TIME_T_LEN + 4,

                          ngx_http_log_request_time },

    { ngx_string("status"), NGX_INT_T_LEN, ngx_http_log_status },

    { ngx_string("bytes_sent"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent },

    { ngx_string("body_bytes_sent"), NGX_OFF_T_LEN,

                          ngx_http_log_body_bytes_sent },

    { ngx_string("request_length"), NGX_SIZE_T_LEN,

                          ngx_http_log_request_length },

    { ngx_null_string, 0, NULL }

};

static ngx_int_t

‌ngx_http_log_handler(ngx_http_request_t *r)

{

    u_char                   *line, *p;

    size_t                    len, size;

    ssize_t                   n;

    ngx_str_t                 val;

    ngx_uint_t                i, l;

    ngx_http_log_t           *log;

    ngx_http_log_op_t        *op;

    ngx_http_log_buf_t       *buffer;

    ngx_http_log_loc_conf_t  *lcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http log handler");

    lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);

    if (lcf->off) {

        return NGX_OK;

    }

    log = lcf->logs->elts;

    for (l = 0; l < lcf->logs->nelts; l++) {

        if (log[l].filter) {

            if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {

                return NGX_ERROR;

            }

            if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {

                continue;

            }

        }

        if (ngx_time() == log[l].disk_full_time) {

            /*

             * on FreeBSD writing to a full filesystem with enabled softupdates

             * may block process for much longer time than writing to non-full

             * filesystem, so we skip writing to a log for one second

             */

            continue;

        }

        ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);

        len = 0;

        op = log[l].format->ops->elts;

        for (i = 0; i < log[l].format->ops->nelts; i++) {

            if (op[i].len == 0) {

                len += op[i].getlen(r, op[i].data);

            } else {

                len += op[i].len;

            }

        }

        if (log[l].syslog_peer) {

            /* length of syslog's PRI and HEADER message parts */

            len += sizeof("<255>Jan 01 00:00:00 ") - 1

                   + ngx_cycle->hostname.len + 1

                   + log[l].syslog_peer->tag.len + 2;

            goto alloc_line;

        }

        len += NGX_LINEFEED_SIZE;

        buffer = log[l].file ? log[l].file->data : NULL;

        if (buffer) {

            if (len > (size_t) (buffer->last - buffer->pos)) {

                ngx_http_log_write(r, &log[l], buffer->start,

                                   buffer->pos - buffer->start);

                buffer->pos = buffer->start;

            }

            if (len <= (size_t) (buffer->last - buffer->pos)) {

                p = buffer->pos;

                if (buffer->event && p == buffer->start) {

                    ngx_add_timer(buffer->event, buffer->flush);

                }

                for (i = 0; i < log[l].format->ops->nelts; i++) {

                    p = op[i].run(r, p, &op[i]);

                }

                ngx_linefeed(p);

                buffer->pos = p;

                continue;

            }

            if (buffer->event && buffer->event->timer_set) {

                ngx_del_timer(buffer->event);

            }

        }

    alloc_line:

        line = ngx_pnalloc(r->pool, len);

        if (line == NULL) {

            return NGX_ERROR;

        }

        p = line;

        if (log[l].syslog_peer) {

            p = ngx_syslog_add_header(log[l].syslog_peer, line);

        }

        for (i = 0; i < log[l].format->ops->nelts; i++) {

            p = op[i].run(r, p, &op[i]);

        }

        if (log[l].syslog_peer) {

            size = p - line;

            n = ngx_syslog_send(log[l].syslog_peer, line, size);

            if (n < 0) {

                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,

                              "send() to syslog failed");

            } else if ((size_t) n != size) {

                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,

                              "send() to syslog has written only %z of %uz",

                              n, size);

            }

            continue;

        }

        ngx_linefeed(p);

        ngx_http_log_write(r, &log[l], line, p - line);

    }

    return NGX_OK;

}

static void

‌ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,

    size_t len)

{

    u_char              *name;

    time_t               now;

    ssize_t              n;

    ngx_err_t            err;

#if (NGX_ZLIB)

    ngx_http_log_buf_t  *buffer;

#endif

    if (log->script == NULL) {

        name = log->file->name.data;

#if (NGX_ZLIB)

        buffer = log->file->data;

        if (buffer && buffer->gzip) {

            n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip,

                                  r->connection->log);

        } else {

            n = ngx_write_fd(log->file->fd, buf, len);

        }

#else

        n = ngx_write_fd(log->file->fd, buf, len);

#endif

    } else {

        name = NULL;

        n = ngx_http_log_script_write(r, log->script, &name, buf, len);

    }

    if (n == (ssize_t) len) {

        return;

    }

    now = ngx_time();

    if (n == -1) {

        err = ngx_errno;

        if (err == NGX_ENOSPC) {

            log->disk_full_time = now;

        }

        if (now - log->error_log_time > 59) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,

                          ngx_write_fd_n " to \"%s\" failed", name);

            log->error_log_time = now;

        }

        return;

    }

    if (now - log->error_log_time > 59) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                      ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",

                      name, n, len);

        log->error_log_time = now;

    }

}

static ssize_t

‌ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,

    u_char **name, u_char *buf, size_t len)

{

    size_t                     root;

    ssize_t                    n;

    ngx_str_t                  log, path;

    ngx_open_file_info_t       of;

    ngx_http_log_loc_conf_t   *llcf;

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!r->root_tested) {

        /* test root directory existence */

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {

            /* simulate successful logging */

            return len;

        }

        path.data[root] = '\0';

        ngx_memzero(&of, sizeof(ngx_open_file_info_t));

        of.valid = clcf->open_file_cache_valid;

        of.min_uses = clcf->open_file_cache_min_uses;

        of.test_dir = 1;

        of.test_only = 1;

        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) {

            /* simulate successful logging */

            return len;

        }

        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)

            != NGX_OK)

        {

            if (of.err == 0) {

                /* simulate successful logging */

                return len;

            }

            ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,

                          "testing \"%s\" existence failed", path.data);

            /* simulate successful logging */

            return len;

        }

        if (!of.is_dir) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,

                          "testing \"%s\" existence failed", path.data);

            /* simulate successful logging */

            return len;

        }

    }

    if (ngx_http_script_run(r, &log, script->lengths->elts, 1,

                            script->values->elts)

        == NULL)

    {

        /* simulate successful logging */

        return len;

    }

    log.data[log.len - 1] = '\0';

    *name = log.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http log \"%s\"", log.data);

    llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.log = 1;

    of.valid = llcf->open_file_cache_valid;

    of.min_uses = llcf->open_file_cache_min_uses;

    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;

    if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {

        /* simulate successful logging */

        return len;

    }

    if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)

        != NGX_OK)

    {

        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                      "%s \"%s\" failed", of.failed, log.data);

        /* simulate successful logging */

        return len;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http log #%d", of.fd);

    n = ngx_write_fd(of.fd, buf, len);

    return n;

}

#if (NGX_ZLIB)

static ssize_t

‌ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,

    ngx_log_t *log)

{

    int          rc, wbits, memlevel;

    u_char      *out;

    size_t       size;

    ssize_t      n;

    z_stream     zstream;

    ngx_err_t    err;

    ngx_pool_t  *pool;

    wbits = MAX_WBITS;

    memlevel = MAX_MEM_LEVEL - 1;

    while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {

        wbits--;

        memlevel--;

    }

    /*

     * This is a formula from deflateBound() for conservative upper bound of

     * compressed data plus 18 bytes of gzip wrapper.

     */

    size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;

    ngx_memzero(&zstream, sizeof(z_stream));

    pool = ngx_create_pool(256, log);

    if (pool == NULL) {

        /* simulate successful logging */

        return len;

    }

    pool->log = log;

    zstream.zalloc = ngx_http_log_gzip_alloc;

    zstream.zfree = ngx_http_log_gzip_free;

    zstream.opaque = pool;

    out = ngx_pnalloc(pool, size);

    if (out == NULL) {

        goto done;

    }

    zstream.next_in = buf;

    zstream.avail_in = len;

    zstream.next_out = out;

    zstream.avail_out = size;

    rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,

                      Z_DEFAULT_STRATEGY);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);

        goto done;

    }

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,

                   "deflate in: ni:%p no:%p ai:%ud ao:%ud",

                   zstream.next_in, zstream.next_out,

                   zstream.avail_in, zstream.avail_out);

    rc = deflate(&zstream, Z_FINISH);

    if (rc != Z_STREAM_END) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "deflate(Z_FINISH) failed: %d", rc);

        goto done;

    }

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0,

                   "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",

                   zstream.next_in, zstream.next_out,

                   zstream.avail_in, zstream.avail_out,

                   rc);

    size -= zstream.avail_out;

    rc = deflateEnd(&zstream);

    if (rc != Z_OK) {

        ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);

        goto done;

    }

    n = ngx_write_fd(fd, out, size);

    if (n != (ssize_t) size) {

        err = (n == -1) ? ngx_errno : 0;

        ngx_destroy_pool(pool);

        ngx_set_errno(err);

        return -1;

    }

done:

    ngx_destroy_pool(pool);

    /* simulate successful logging */

    return len;

}

static void *

‌ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size)

{

    ngx_pool_t *pool = opaque;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,

                   "gzip alloc: n:%ud s:%ud", items, size);

    return ngx_palloc(pool, items * size);

}

static void

‌ngx_http_log_gzip_free(void *opaque, void *address)

{

#if 0

    ngx_pool_t *pool = opaque;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "gzip free: %p", address);

#endif

}

#endif

static void

‌ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log)

{

    size_t               len;

    ssize_t              n;

    ngx_http_log_buf_t  *buffer;

    buffer = file->data;

    len = buffer->pos - buffer->start;

    if (len == 0) {

        return;

    }

#if (NGX_ZLIB)

    if (buffer->gzip) {

        n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log);

    } else {

        n = ngx_write_fd(file->fd, buffer->start, len);

    }

#else

    n = ngx_write_fd(file->fd, buffer->start, len);

#endif

    if (n == -1) {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      ngx_write_fd_n " to \"%s\" failed",

                      file->name.data);

    } else if ((size_t) n != len) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",

                      file->name.data, n, len);

    }

    buffer->pos = buffer->start;

    if (buffer->event && buffer->event->timer_set) {

        ngx_del_timer(buffer->event);

    }

}

static void

‌ngx_http_log_flush_handler(ngx_event_t *ev)

{

    ngx_open_file_t     *file;

    ngx_http_log_buf_t  *buffer;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "http log buffer flush handler");

    if (ev->timedout) {

        ngx_http_log_flush(ev->data, ev->log);

        return;

    }

    /* cancel the flush timer for graceful shutdown */

    file = ev->data;

    buffer = file->data;

    buffer->event = NULL;

}

static u_char *

‌ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    size_t     len;

    uintptr_t  data;

    len = op->len;

    data = op->data;

    while (len--) {

        *buf++ = (u_char) (data & 0xff);

        data >>= 8;

    }

    return buf;

}

static u_char *

‌ngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    return ngx_cpymem(buf, (u_char *) op->data, op->len);

}

static u_char *

‌ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    if (r->pipeline) {

        *buf = 'p';

    } else {

        *buf = '.';

    }

    return buf + 1;

}

static u_char *

‌ngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    return ngx_cpymem(buf, ngx_cached_http_log_time.data,

                      ngx_cached_http_log_time.len);

}

static u_char *

‌ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data,

                      ngx_cached_http_log_iso8601.len);

}

static u_char *

‌ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    ngx_time_t  *tp;

    tp = ngx_timeofday();

    return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);

}

static u_char *

‌ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    ngx_time_t      *tp;

    ngx_msec_int_t   ms;

    tp = ngx_timeofday();

    ms = (ngx_msec_int_t)

             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));

    ms = ngx_max(ms, 0);

    return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000);

}

static u_char *

‌ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    ngx_uint_t  status;

    if (r->err_status) {

        status = r->err_status;

    } else if (r->headers_out.status) {

        status = r->headers_out.status;

    } else if (r->http_version == NGX_HTTP_VERSION_9) {

        status = 9;

    } else {

        status = 0;

    }

    return ngx_sprintf(buf, "%03ui", status);

}

static u_char *

‌ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    return ngx_sprintf(buf, "%O", r->connection->sent);

}

/*

* although there is a real $body_bytes_sent variable,

* this log operation code function is more optimized for logging

*/

static u_char *

‌ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    off_t  length;

    length = r->connection->sent - r->header_size;

    if (length > 0) {

        return ngx_sprintf(buf, "%O", length);

    }

    *buf = '0';

    return buf + 1;

}

static u_char *

‌ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,

    ngx_http_log_op_t *op)

{

    return ngx_sprintf(buf, "%O", r->request_length);

}

static ngx_int_t

‌ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,

    ngx_str_t *value)

{

    ngx_int_t  index;

    index = ngx_http_get_variable_index(cf, value);

    if (index == NGX_ERROR) {

        return NGX_ERROR;

    }

    op->len = 0;

    op->getlen = ngx_http_log_variable_getlen;

    op->run = ngx_http_log_variable;

    op->data = index;

    return NGX_OK;

}

static size_t

‌ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)

{

    uintptr_t                   len;

    ngx_http_variable_value_t  *value;

    value = ngx_http_get_indexed_variable(r, data);

    if (value == NULL || value->not_found) {

        return 1;

    }

    len = ngx_http_log_escape(NULL, value->data, value->len);

    value->escape = len ? 1 : 0;

    return value->len + len * 3;

}

static u_char *

‌ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)

{

    ngx_http_variable_value_t  *value;

    value = ngx_http_get_indexed_variable(r, op->data);

    if (value == NULL || value->not_found) {

        *buf = '-';

        return buf + 1;

    }

    if (value->escape == 0) {

        return ngx_cpymem(buf, value->data, value->len);

    } else {

        return (u_char *) ngx_http_log_escape(buf, value->data, value->len);

    }

}

static uintptr_t

‌ngx_http_log_escape(u_char *dst, u_char *src, size_t size)

{

    ngx_uint_t      n;

    static u_char   hex[] = "0123456789ABCDEF";

    static uint32_t   escape[] = {

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */

        0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */

        0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */

        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    };

    if (dst == NULL) {

        /* find the number of the characters to be escaped */

        n = 0;

        while (size) {

            if (escape[*src >> 5] & (1 << (*src & 0x1f))) {

                n++;

            }

            src++;

            size--;

        }

        return (uintptr_t) n;

    }

    while (size) {

        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {

            *dst++ = '\\';

            *dst++ = 'x';

            *dst++ = hex[*src >> 4];

            *dst++ = hex[*src & 0xf];

            src++;

        } else {

            *dst++ = *src++;

        }

        size--;

    }

    return (uintptr_t) dst;

}

static void *

‌ngx_http_log_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_log_main_conf_t  *conf;

    ngx_http_log_fmt_t  *fmt;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))

        != NGX_OK)

    {

        return NULL;

    }

    fmt = ngx_array_push(&conf->formats);

    if (fmt == NULL) {

        return NULL;

    }

    ngx_str_set(&fmt->name, "combined");

    fmt->flushes = NULL;

    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));

    if (fmt->ops == NULL) {

        return NULL;

    }

    return conf;

}

static void *

‌ngx_http_log_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_log_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->open_file_cache = NGX_CONF_UNSET_PTR;

    return conf;

}

static char *

‌ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_log_loc_conf_t *prev = parent;

    ngx_http_log_loc_conf_t *conf = child;

    ngx_http_log_t            *log;

    ngx_http_log_fmt_t        *fmt;

    ngx_http_log_main_conf_t  *lmcf;

    if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {

        conf->open_file_cache = prev->open_file_cache;

        conf->open_file_cache_valid = prev->open_file_cache_valid;

        conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;

        if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {

            conf->open_file_cache = NULL;

        }

    }

    if (conf->logs || conf->off) {

        return NGX_CONF_OK;

    }

    conf->logs = prev->logs;

    conf->off = prev->off;

    if (conf->logs || conf->off) {

        return NGX_CONF_OK;

    }

    conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));

    if (conf->logs == NULL) {

        return NGX_CONF_ERROR;

    }

    log = ngx_array_push(conf->logs);

    if (log == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(log, sizeof(ngx_http_log_t));

    log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log);

    if (log->file == NULL) {

        return NGX_CONF_ERROR;

    }

    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);

    fmt = lmcf->formats.elts;

    /* the default "combined" format */

    log->format = &fmt[0];

    lmcf->combined_used = 1;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_log_loc_conf_t *llcf = conf;

    ssize_t                            size;

    ngx_int_t                          gzip;

    ngx_uint_t                         i, n;

    ngx_msec_t                         flush;

    ngx_str_t                         *value, name, s;

    ngx_http_log_t                    *log;

    ngx_syslog_peer_t                 *peer;

    ngx_http_log_buf_t                *buffer;

    ngx_http_log_fmt_t                *fmt;

    ngx_http_log_main_conf_t          *lmcf;

    ngx_http_script_compile_t          sc;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        llcf->off = 1;

        if (cf->args->nelts == 2) {

            return NGX_CONF_OK;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[2]);

        return NGX_CONF_ERROR;

    }

    if (llcf->logs == NULL) {

        llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));

        if (llcf->logs == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);

    log = ngx_array_push(llcf->logs);

    if (log == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(log, sizeof(ngx_http_log_t));

    if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {

        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));

        if (peer == NULL) {

            return NGX_CONF_ERROR;

        }

        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

        log->syslog_peer = peer;

        goto process_formats;

    }

    n = ngx_http_script_variables_count(&value[1]);

    if (n == 0) {

        log->file = ngx_conf_open_file(cf->cycle, &value[1]);

        if (log->file == NULL) {

            return NGX_CONF_ERROR;

        }

    } else {

        if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));

        if (log->script == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &value[1];

        sc.lengths = &log->script->lengths;

        sc.values = &log->script->values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

process_formats:

    if (cf->args->nelts >= 3) {

        name = value[2];

        if (ngx_strcmp(name.data, "combined") == 0) {

            lmcf->combined_used = 1;

        }

    } else {

        ngx_str_set(&name, "combined");

        lmcf->combined_used = 1;

    }

    fmt = lmcf->formats.elts;

    for (i = 0; i < lmcf->formats.nelts; i++) {

        if (fmt[i].name.len == name.len

            && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)

        {

            log->format = &fmt[i];

            break;

        }

    }

    if (log->format == NULL) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "unknown log format \"%V\"", &name);

        return NGX_CONF_ERROR;

    }

    size = 0;

    flush = 0;

    gzip = 0;

    for (i = 3; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {

            s.len = value[i].len - 7;

            s.data = value[i].data + 7;

            size = ngx_parse_size(&s);

            if (size == NGX_ERROR || size == 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid buffer size \"%V\"", &s);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {

            s.len = value[i].len - 6;

            s.data = value[i].data + 6;

            flush = ngx_parse_time(&s, 0);

            if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid flush time \"%V\"", &s);

                return NGX_CONF_ERROR;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "gzip", 4) == 0

            && (value[i].len == 4 || value[i].data[4] == '='))

        {

#if (NGX_ZLIB)

            if (size == 0) {

                size = 64 * 1024;

            }

            if (value[i].len == 4) {

                gzip = Z_BEST_SPEED;

                continue;

            }

            s.len = value[i].len - 5;

            s.data = value[i].data + 5;

            gzip = ngx_atoi(s.data, s.len);

            if (gzip < 1 || gzip > 9) {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "invalid compression level \"%V\"", &s);

                return NGX_CONF_ERROR;

            }

            continue;

#else

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "nginx was built without zlib support");

            return NGX_CONF_ERROR;

#endif

        }

        if (ngx_strncmp(value[i].data, "if=", 3) == 0) {

            s.len = value[i].len - 3;

            s.data = value[i].data + 3;

            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

            ccv.cf = cf;

            ccv.value = &s;

            ccv.complex_value = ngx_palloc(cf->pool,

                                           sizeof(ngx_http_complex_value_t));

            if (ccv.complex_value == NULL) {

                return NGX_CONF_ERROR;

            }

            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

            log->filter = ccv.complex_value;

            continue;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[i]);

        return NGX_CONF_ERROR;

    }

    if (flush && size == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "no buffer is defined for access_log \"%V\"",

                           &value[1]);

        return NGX_CONF_ERROR;

    }

    if (size) {

        if (log->script) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "buffered logs cannot have variables in name");

            return NGX_CONF_ERROR;

        }

        if (log->syslog_peer) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "logs to syslog cannot be buffered");

            return NGX_CONF_ERROR;

        }

        if (log->file->data) {

            buffer = log->file->data;

            if (buffer->last - buffer->start != size

                || buffer->flush != flush

                || buffer->gzip != gzip)

            {

                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                   "access_log \"%V\" already defined "

                                   "with conflicting parameters",

                                   &value[1]);

                return NGX_CONF_ERROR;

            }

            return NGX_CONF_OK;

        }

        buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t));

        if (buffer == NULL) {

            return NGX_CONF_ERROR;

        }

        buffer->start = ngx_pnalloc(cf->pool, size);

        if (buffer->start == NULL) {

            return NGX_CONF_ERROR;

        }

        buffer->pos = buffer->start;

        buffer->last = buffer->start + size;

        if (flush) {

            buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));

            if (buffer->event == NULL) {

                return NGX_CONF_ERROR;

            }

            buffer->event->data = log->file;

            buffer->event->handler = ngx_http_log_flush_handler;

            buffer->event->log = &cf->cycle->new_log;

            buffer->event->cancelable = 1;

            buffer->flush = flush;

        }

        buffer->gzip = gzip;

        log->file->flush = ngx_http_log_flush;

        log->file->data = buffer;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_log_main_conf_t *lmcf = conf;

    ngx_str_t           *value;

    ngx_uint_t           i;

    ngx_http_log_fmt_t  *fmt;

    value = cf->args->elts;

    fmt = lmcf->formats.elts;

    for (i = 0; i < lmcf->formats.nelts; i++) {

        if (fmt[i].name.len == value[1].len

            && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)

        {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "duplicate \"log_format\" name \"%V\"",

                               &value[1]);

            return NGX_CONF_ERROR;

        }

    }

    fmt = ngx_array_push(&lmcf->formats);

    if (fmt == NULL) {

        return NGX_CONF_ERROR;

    }

    fmt->name = value[1];

    fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));

    if (fmt->flushes == NULL) {

        return NGX_CONF_ERROR;

    }

    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));

    if (fmt->ops == NULL) {

        return NGX_CONF_ERROR;

    }

    return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2);

}

static char *

‌ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,

    ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)

{

    u_char              *data, *p, ch;

    size_t               i, len;

    ngx_str_t           *value, var;

    ngx_int_t           *flush;

    ngx_uint_t           bracket;

    ngx_http_log_op_t   *op;

    ngx_http_log_var_t  *v;

    value = args->elts;

    for ( /* void */ ; s < args->nelts; s++) {

        i = 0;

        while (i < value[s].len) {

            op = ngx_array_push(ops);

            if (op == NULL) {

                return NGX_CONF_ERROR;

            }

            data = &value[s].data[i];

            if (value[s].data[i] == '$') {

                if (++i == value[s].len) {

                    goto invalid;

                }

                if (value[s].data[i] == '{') {

                    bracket = 1;

                    if (++i == value[s].len) {

                        goto invalid;

                    }

                    var.data = &value[s].data[i];

                } else {

                    bracket = 0;

                    var.data = &value[s].data[i];

                }

                for (var.len = 0; i < value[s].len; i++, var.len++) {

                    ch = value[s].data[i];

                    if (ch == '}' && bracket) {

                        i++;

                        bracket = 0;

                        break;

                    }

                    if ((ch >= 'A' && ch <= 'Z')

                        || (ch >= 'a' && ch <= 'z')

                        || (ch >= '0' && ch <= '9')

                        || ch == '_')

                    {

                        continue;

                    }

                    break;

                }

                if (bracket) {

                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                                       "the closing bracket in \"%V\" "

                                       "variable is missing", &var);

                    return NGX_CONF_ERROR;

                }

                if (var.len == 0) {

                    goto invalid;

                }

                for (v = ngx_http_log_vars; v->name.len; v++) {

                    if (v->name.len == var.len

                        && ngx_strncmp(v->name.data, var.data, var.len) == 0)

                    {

                        op->len = v->len;

                        op->getlen = NULL;

                        op->run = v->run;

                        op->data = 0;

                        goto found;

                    }

                }

                if (ngx_http_log_variable_compile(cf, op, &var) != NGX_OK) {

                    return NGX_CONF_ERROR;

                }

                if (flushes) {

                    flush = ngx_array_push(flushes);

                    if (flush == NULL) {

                        return NGX_CONF_ERROR;

                    }

                    *flush = op->data; /* variable index */

                }

            found:

                continue;

            }

            i++;

            while (i < value[s].len && value[s].data[i] != '$') {

                i++;

            }

            len = &value[s].data[i] - data;

            if (len) {

                op->len = len;

                op->getlen = NULL;

                if (len <= sizeof(uintptr_t)) {

                    op->run = ngx_http_log_copy_short;

                    op->data = 0;

                    while (len--) {

                        op->data <<= 8;

                        op->data |= data[len];

                    }

                } else {

                    op->run = ngx_http_log_copy_long;

                    p = ngx_pnalloc(cf->pool, len);

                    if (p == NULL) {

                        return NGX_CONF_ERROR;

                    }

                    ngx_memcpy(p, data, len);

                    op->data = (uintptr_t) p;

                }

            }

        }

    }

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_log_loc_conf_t *llcf = conf;

    time_t       inactive, valid;

    ngx_str_t   *value, s;

    ngx_int_t    max, min_uses;

    ngx_uint_t   i;

    if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    max = 0;

    inactive = 10;

    valid = 60;

    min_uses = 1;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {

            max = ngx_atoi(value[i].data + 4, value[i].len - 4);

            if (max == NGX_ERROR) {

                goto failed;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {

            s.len = value[i].len - 9;

            s.data = value[i].data + 9;

            inactive = ngx_parse_time(&s, 1);

            if (inactive == (time_t) NGX_ERROR) {

                goto failed;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {

            min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);

            if (min_uses == NGX_ERROR) {

                goto failed;

            }

            continue;

        }

        if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {

            s.len = value[i].len - 6;

            s.data = value[i].data + 6;

            valid = ngx_parse_time(&s, 1);

            if (valid == (time_t) NGX_ERROR) {

                goto failed;

            }

            continue;

        }

        if (ngx_strcmp(value[i].data, "off") == 0) {

            llcf->open_file_cache = NULL;

            continue;

        }

    failed:

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid \"open_log_file_cache\" parameter \"%V\"",

                           &value[i]);

        return NGX_CONF_ERROR;

    }

    if (llcf->open_file_cache == NULL) {

        return NGX_CONF_OK;

    }

    if (max == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "\"open_log_file_cache\" must have \"max\" parameter");

        return NGX_CONF_ERROR;

    }

    llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);

    if (llcf->open_file_cache) {

        llcf->open_file_cache_valid = valid;

        llcf->open_file_cache_min_uses = min_uses;

        return NGX_CONF_OK;

    }

    return NGX_CONF_ERROR;

}

static ngx_int_t

‌ngx_http_log_init(ngx_conf_t *cf)

{

    ngx_str_t                  *value;

    ngx_array_t                 a;

    ngx_http_handler_pt        *h;

    ngx_http_log_fmt_t         *fmt;

    ngx_http_log_main_conf_t   *lmcf;

    ngx_http_core_main_conf_t  *cmcf;

    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);

    if (lmcf->combined_used) {

        if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {

            return NGX_ERROR;

        }

        value = ngx_array_push(&a);

        if (value == NULL) {

            return NGX_ERROR;

        }

        *value = ngx_http_combined_fmt;

        fmt = lmcf->formats.elts;

        if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)

            != NGX_CONF_OK)

        {

            return NGX_ERROR;

        }

    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_log_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_map_module.c - nginx-1.7.9

Global variables defined

ngx_http_map_commands

ngx_http_map_module

ngx_http_map_module_ctx

Data types defined

ngx_http_map_conf_ctx_t

ngx_http_map_conf_t

ngx_http_map_ctx_t

Functions defined

ngx_http_map

ngx_http_map_block

ngx_http_map_cmp_dns_wildcards

ngx_http_map_create_conf

ngx_http_map_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_uint_t                  hash_max_size;

    ngx_uint_t                  hash_bucket_size;

‌} ngx_http_map_conf_t;

typedef struct {

    ngx_hash_keys_arrays_t      keys;

    ngx_array_t                *values_hash;

    ngx_array_t                 var_values;

#if (NGX_PCRE)

    ngx_array_t                 regexes;

#endif

    ngx_http_variable_value_t  *default_value;

    ngx_conf_t                 *cf;

    ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */

‌} ngx_http_map_conf_ctx_t;

typedef struct {

    ngx_http_map_t              map;

    ngx_http_complex_value_t    value;

    ngx_http_variable_value_t  *default_value;

    ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */

‌} ngx_http_map_ctx_t;

static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,

    const void *two);

static void *ngx_http_map_create_conf(ngx_conf_t *cf);

static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);

‌static ngx_command_t  ngx_http_map_commands[] = {

    { ngx_string("map"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,

      ngx_http_map_block,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("map_hash_max_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_map_conf_t, hash_max_size),

      NULL },

    { ngx_string("map_hash_bucket_size"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_map_conf_t, hash_bucket_size),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_map_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_map_create_conf,              /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_map_module = {

    NGX_MODULE_V1,

    &ngx_http_map_module_ctx,              /* module context */

    ngx_http_map_commands,                 /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_map_ctx_t  *map = (ngx_http_map_ctx_t *) data;

    ngx_str_t                   val;

    ngx_http_variable_value_t  *value;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http map started");

    if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {

        return NGX_ERROR;

    }

    if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {

        val.len--;

    }

    value = ngx_http_map_find(r, &map->map, &val);

    if (value == NULL) {

        value = map->default_value;

    }

    if (!value->valid) {

        value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data);

        if (value == NULL || value->not_found) {

            value = &ngx_http_variable_null_value;

        }

    }

    *v = *value;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http map: \"%v\" \"%v\"", &val, v);

    return NGX_OK;

}

static void *

‌ngx_http_map_create_conf(ngx_conf_t *cf)

{

    ngx_http_map_conf_t  *mcf;

    mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));

    if (mcf == NULL) {

        return NULL;

    }

    mcf->hash_max_size = NGX_CONF_UNSET_UINT;

    mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;

    return mcf;

}

static char *

‌ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_map_conf_t  *mcf = conf;

    char                              *rv;

    ngx_str_t                         *value, name;

    ngx_conf_t                         save;

    ngx_pool_t                        *pool;

    ngx_hash_init_t                    hash;

    ngx_http_map_ctx_t                *map;

    ngx_http_variable_t               *var;

    ngx_http_map_conf_ctx_t            ctx;

    ngx_http_compile_complex_value_t   ccv;

    if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {

        mcf->hash_max_size = 2048;

    }

    if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {

        mcf->hash_bucket_size = ngx_cacheline_size;

    } else {

        mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,

                                          ngx_cacheline_size);

    }

    map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));

    if (map == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &map->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    name = value[2];

    if (name.data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &name);

        return NGX_CONF_ERROR;

    }

    name.len--;

    name.data++;

    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    var->get_handler = ngx_http_map_variable;

    var->data = (uintptr_t) map;

    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);

    if (pool == NULL) {

        return NGX_CONF_ERROR;

    }

    ctx.keys.pool = cf->pool;

    ctx.keys.temp_pool = pool;

    if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {

        ngx_destroy_pool(pool);

        return NGX_CONF_ERROR;

    }

    ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);

    if (ctx.values_hash == NULL) {

        ngx_destroy_pool(pool);

        return NGX_CONF_ERROR;

    }

    if (ngx_array_init(&ctx.var_values, cf->pool, 2,

                       sizeof(ngx_http_variable_value_t))

        != NGX_OK)

    {

        ngx_destroy_pool(pool);

        return NGX_CONF_ERROR;

    }

#if (NGX_PCRE)

    if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))

        != NGX_OK)

    {

        ngx_destroy_pool(pool);

        return NGX_CONF_ERROR;

    }

#endif

    ctx.default_value = NULL;

    ctx.cf = &save;

    ctx.hostnames = 0;

    save = *cf;

    cf->pool = pool;

    cf->ctx = &ctx;

    cf->handler = ngx_http_map;

    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    if (rv != NGX_CONF_OK) {

        ngx_destroy_pool(pool);

        return rv;

    }

    map->default_value = ctx.default_value ? ctx.default_value:

                                             &ngx_http_variable_null_value;

    map->hostnames = ctx.hostnames;

    hash.key = ngx_hash_key_lc;

    hash.max_size = mcf->hash_max_size;

    hash.bucket_size = mcf->hash_bucket_size;

    hash.name = "map_hash";

    hash.pool = cf->pool;

    if (ctx.keys.keys.nelts) {

        hash.hash = &map->map.hash.hash;

        hash.temp_pool = NULL;

        if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)

            != NGX_OK)

        {

            ngx_destroy_pool(pool);

            return NGX_CONF_ERROR;

        }

    }

    if (ctx.keys.dns_wc_head.nelts) {

        ngx_qsort(ctx.keys.dns_wc_head.elts,

                  (size_t) ctx.keys.dns_wc_head.nelts,

                  sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);

        hash.hash = NULL;

        hash.temp_pool = pool;

        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,

                                   ctx.keys.dns_wc_head.nelts)

            != NGX_OK)

        {

            ngx_destroy_pool(pool);

            return NGX_CONF_ERROR;

        }

        map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;

    }

    if (ctx.keys.dns_wc_tail.nelts) {

        ngx_qsort(ctx.keys.dns_wc_tail.elts,

                  (size_t) ctx.keys.dns_wc_tail.nelts,

                  sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);

        hash.hash = NULL;

        hash.temp_pool = pool;

        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,

                                   ctx.keys.dns_wc_tail.nelts)

            != NGX_OK)

        {

            ngx_destroy_pool(pool);

            return NGX_CONF_ERROR;

        }

        map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;

    }

#if (NGX_PCRE)

    if (ctx.regexes.nelts) {

        map->map.regex = ctx.regexes.elts;

        map->map.nregex = ctx.regexes.nelts;

    }

#endif

    ngx_destroy_pool(pool);

    return rv;

}

static int ngx_libc_cdecl

‌ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)

{

    ngx_hash_key_t  *first, *second;

    first = (ngx_hash_key_t *) one;

    second = (ngx_hash_key_t *) two;

    return ngx_dns_strcmp(first->key.data, second->key.data);

}

static char *

‌ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)

{

    ngx_int_t                   rc, index;

    ngx_str_t                  *value, name;

    ngx_uint_t                  i, key;

    ngx_http_map_conf_ctx_t    *ctx;

    ngx_http_variable_value_t  *var, **vp;

    ctx = cf->ctx;

    value = cf->args->elts;

    if (cf->args->nelts == 1

        && ngx_strcmp(value[0].data, "hostnames") == 0)

    {

        ctx->hostnames = 1;

        return NGX_CONF_OK;

    } else if (cf->args->nelts != 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid number of the map parameters");

        return NGX_CONF_ERROR;

    }

    if (ngx_strcmp(value[0].data, "include") == 0) {

        return ngx_conf_include(cf, dummy, conf);

    }

    if (value[1].data[0] == '$') {

        name = value[1];

        name.len--;

        name.data++;

        index = ngx_http_get_variable_index(ctx->cf, &name);

        if (index == NGX_ERROR) {

            return NGX_CONF_ERROR;

        }

        var = ctx->var_values.elts;

        for (i = 0; i < ctx->var_values.nelts; i++) {

            if (index == (intptr_t) var[i].data) {

                var = &var[i];

                goto found;

            }

        }

        var = ngx_array_push(&ctx->var_values);

        if (var == NULL) {

            return NGX_CONF_ERROR;

        }

        var->valid = 0;

        var->no_cacheable = 0;

        var->not_found = 0;

        var->len = 0;

        var->data = (u_char *) (intptr_t) index;

        goto found;

    }

    key = 0;

    for (i = 0; i < value[1].len; i++) {

        key = ngx_hash(key, value[1].data[i]);

    }

    key %= ctx->keys.hsize;

    vp = ctx->values_hash[key].elts;

    if (vp) {

        for (i = 0; i < ctx->values_hash[key].nelts; i++) {

            if (value[1].len != (size_t) vp[i]->len) {

                continue;

            }

            if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {

                var = vp[i];

                goto found;

            }

        }

    } else {

        if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,

                           sizeof(ngx_http_variable_value_t *))

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    var->len = value[1].len;

    var->data = ngx_pstrdup(ctx->keys.pool, &value[1]);

    if (var->data == NULL) {

        return NGX_CONF_ERROR;

    }

    var->valid = 1;

    var->no_cacheable = 0;

    var->not_found = 0;

    vp = ngx_array_push(&ctx->values_hash[key]);

    if (vp == NULL) {

        return NGX_CONF_ERROR;

    }

    *vp = var;

found:

    if (ngx_strcmp(value[0].data, "default") == 0) {

        if (ctx->default_value) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "duplicate default map parameter");

            return NGX_CONF_ERROR;

        }

        ctx->default_value = var;

        return NGX_CONF_OK;

    }

#if (NGX_PCRE)

    if (value[0].len && value[0].data[0] == '~') {

        ngx_regex_compile_t    rc;

        ngx_http_map_regex_t  *regex;

        u_char                 errstr[NGX_MAX_CONF_ERRSTR];

        regex = ngx_array_push(&ctx->regexes);

        if (regex == NULL) {

            return NGX_CONF_ERROR;

        }

        value[0].len--;

        value[0].data++;

        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

        if (value[0].data[0] == '*') {

            value[0].len--;

            value[0].data++;

            rc.options = NGX_REGEX_CASELESS;

        }

        rc.pattern = value[0];

        rc.err.len = NGX_MAX_CONF_ERRSTR;

        rc.err.data = errstr;

        regex->regex = ngx_http_regex_compile(ctx->cf, &rc);

        if (regex->regex == NULL) {

            return NGX_CONF_ERROR;

        }

        regex->value = var;

        return NGX_CONF_OK;

    }

#endif

    if (value[0].len && value[0].data[0] == '\\') {

        value[0].len--;

        value[0].data++;

    }

    rc = ngx_hash_add_key(&ctx->keys, &value[0], var,

                          (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);

    if (rc == NGX_OK) {

        return NGX_CONF_OK;

    }

    if (rc == NGX_DECLINED) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid hostname or wildcard \"%V\"", &value[0]);

    }

    if (rc == NGX_BUSY) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "conflicting parameter \"%V\"", &value[0]);

    }

    return NGX_CONF_ERROR;

}

src/http/modules/ngx_http_memcached_module.c - nginx-1.7.9

Global variables defined

ngx_http_memcached_commands

ngx_http_memcached_end

ngx_http_memcached_key

ngx_http_memcached_module

ngx_http_memcached_module_ctx

ngx_http_memcached_next_upstream_masks

Data types defined

ngx_http_memcached_ctx_t

ngx_http_memcached_loc_conf_t

Functions defined

ngx_http_memcached_abort_request

ngx_http_memcached_create_loc_conf

ngx_http_memcached_create_request

ngx_http_memcached_filter

ngx_http_memcached_filter_init

ngx_http_memcached_finalize_request

ngx_http_memcached_handler

ngx_http_memcached_merge_loc_conf

ngx_http_memcached_pass

ngx_http_memcached_process_header

ngx_http_memcached_reinit_request

Macros defined

NGX_HTTP_MEMCACHED_END

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_http_upstream_conf_t   upstream;

    ngx_int_t                  index;

    ngx_uint_t                 gzip_flag;

‌} ngx_http_memcached_loc_conf_t;

typedef struct {

    size_t                     rest;

    ngx_http_request_t        *request;

    ngx_str_t                  key;

‌} ngx_http_memcached_ctx_t;

static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_memcached_filter_init(void *data);

static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);

static void ngx_http_memcached_abort_request(ngx_http_request_t *r);

static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,

    ngx_int_t rc);

static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {

    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },

    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },

    { ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },

    { ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },

    { ngx_null_string, 0 }

};

‌static ngx_command_t  ngx_http_memcached_commands[] = {

    { ngx_string("memcached_pass"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_memcached_pass,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("memcached_bind"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_bind_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.local),

      NULL },

    { ngx_string("memcached_connect_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),

      NULL },

    { ngx_string("memcached_send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),

      NULL },

    { ngx_string("memcached_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),

      NULL },

    { ngx_string("memcached_read_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),

      NULL },

    { ngx_string("memcached_next_upstream"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),

      &ngx_http_memcached_next_upstream_masks },

    { ngx_string("memcached_next_upstream_tries"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_tries),

      NULL },

    { ngx_string("memcached_next_upstream_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),

      NULL },

    { ngx_string("memcached_gzip_flag"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_memcached_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_memcached_create_loc_conf,    /* create location configuration */

    ngx_http_memcached_merge_loc_conf      /* merge location configuration */

};

‌ngx_module_t  ngx_http_memcached_module = {

    NGX_MODULE_V1,

    &ngx_http_memcached_module_ctx,        /* module context */

    ngx_http_memcached_commands,           /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  ngx_http_memcached_key = ngx_string("memcached_key");

‌#define NGX_HTTP_MEMCACHED_END   (sizeof(ngx_http_memcached_end) - 1)

‌static u_char  ngx_http_memcached_end[] = CRLF "END" CRLF;

static ngx_int_t

‌ngx_http_memcached_handler(ngx_http_request_t *r)

{

    ngx_int_t                       rc;

    ngx_http_upstream_t            *u;

    ngx_http_memcached_ctx_t       *ctx;

    ngx_http_memcached_loc_conf_t  *mlcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_HTTP_NOT_ALLOWED;

    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {

        return rc;

    }

    if (ngx_http_set_content_type(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_http_upstream_create(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    u = r->upstream;

    ngx_str_set(&u->schema, "memcached://");

    u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

    u->conf = &mlcf->upstream;

    u->create_request = ngx_http_memcached_create_request;

    u->reinit_request = ngx_http_memcached_reinit_request;

    u->process_header = ngx_http_memcached_process_header;

    u->abort_request = ngx_http_memcached_abort_request;

    u->finalize_request = ngx_http_memcached_finalize_request;

    ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));

    if (ctx == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ctx->request = r;

    ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);

    u->input_filter_init = ngx_http_memcached_filter_init;

    u->input_filter = ngx_http_memcached_filter;

    u->input_filter_ctx = ctx;

    r->main->count++;

    ngx_http_upstream_init(r);

    return NGX_DONE;

}

static ngx_int_t

‌ngx_http_memcached_create_request(ngx_http_request_t *r)

{

    size_t                          len;

    uintptr_t                       escape;

    ngx_buf_t                      *b;

    ngx_chain_t                    *cl;

    ngx_http_memcached_ctx_t       *ctx;

    ngx_http_variable_value_t      *vv;

    ngx_http_memcached_loc_conf_t  *mlcf;

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

    vv = ngx_http_get_indexed_variable(r, mlcf->index);

    if (vv == NULL || vv->not_found || vv->len == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "the \"$memcached_key\" variable is not set");

        return NGX_ERROR;

    }

    escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);

    len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    cl->next = NULL;

    r->upstream->request_bufs = cl;

    *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';

    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);

    ctx->key.data = b->last;

    if (escape == 0) {

        b->last = ngx_copy(b->last, vv->data, vv->len);

    } else {

        b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,

                                            NGX_ESCAPE_MEMCACHED);

    }

    ctx->key.len = b->last - ctx->key.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http memcached request: \"%V\"", &ctx->key);

    *b->last++ = CR; *b->last++ = LF;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_memcached_reinit_request(ngx_http_request_t *r)

{

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_memcached_process_header(ngx_http_request_t *r)

{

    u_char                         *p, *start;

    ngx_str_t                       line;

    ngx_uint_t                      flags;

    ngx_table_elt_t                *h;

    ngx_http_upstream_t            *u;

    ngx_http_memcached_ctx_t       *ctx;

    ngx_http_memcached_loc_conf_t  *mlcf;

    u = r->upstream;

    for (p = u->buffer.pos; p < u->buffer.last; p++) {

        if (*p == LF) {

            goto found;

        }

    }

    return NGX_AGAIN;

found:

    line.data = u->buffer.pos;

    line.len = p - u->buffer.pos;

    if (line.len == 0 || *(p - 1) != CR) {

        goto no_valid;

    }

    *p = '\0';

    line.len--;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "memcached: \"%V\"", &line);

    p = u->buffer.pos;

    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

    if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {

        p += sizeof("VALUE ") - 1;

        if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "memcached sent invalid key in response \"%V\" "

                          "for key \"%V\"",

                          &line, &ctx->key);

            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

        }

        p += ctx->key.len;

        if (*p++ != ' ') {

            goto no_valid;

        }

        /* flags */

        start = p;

        while (*p) {

            if (*p++ == ' ') {

                if (mlcf->gzip_flag) {

                    goto flags;

                } else {

                    goto length;

                }

            }

        }

        goto no_valid;

    flags:

        flags = ngx_atoi(start, p - start - 1);

        if (flags == (ngx_uint_t) NGX_ERROR) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "memcached sent invalid flags in response \"%V\" "

                          "for key \"%V\"",

                          &line, &ctx->key);

            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

        }

        if (flags & mlcf->gzip_flag) {

            h = ngx_list_push(&r->headers_out.headers);

            if (h == NULL) {

                return NGX_ERROR;

            }

            h->hash = 1;

            ngx_str_set(&h->key, "Content-Encoding");

            ngx_str_set(&h->value, "gzip");

            r->headers_out.content_encoding = h;

        }

    length:

        start = p;

        p = line.data + line.len;

        u->headers_in.content_length_n = ngx_atoof(start, p - start);

        if (u->headers_in.content_length_n == NGX_ERROR) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "memcached sent invalid length in response \"%V\" "

                          "for key \"%V\"",

                          &line, &ctx->key);

            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

        }

        u->headers_in.status_n = 200;

        u->state->status = 200;

        u->buffer.pos = p + sizeof(CRLF) - 1;

        return NGX_OK;

    }

    if (ngx_strcmp(p, "END\x0d") == 0) {

        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,

                      "key: \"%V\" was not found by memcached", &ctx->key);

        u->headers_in.content_length_n = 0;

        u->headers_in.status_n = 404;

        u->state->status = 404;

        u->buffer.pos = p + sizeof("END" CRLF) - 1;

        u->keepalive = 1;

        return NGX_OK;

    }

no_valid:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "memcached sent invalid response: \"%V\"", &line);

    return NGX_HTTP_UPSTREAM_INVALID_HEADER;

}

static ngx_int_t

‌ngx_http_memcached_filter_init(void *data)

{

    ngx_http_memcached_ctx_t  *ctx = data;

    ngx_http_upstream_t  *u;

    u = ctx->request->upstream;

    if (u->headers_in.status_n != 404) {

        u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;

        ctx->rest = NGX_HTTP_MEMCACHED_END;

    } else {

        u->length = 0;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_memcached_filter(void *data, ssize_t bytes)

{

    ngx_http_memcached_ctx_t  *ctx = data;

    u_char               *last;

    ngx_buf_t            *b;

    ngx_chain_t          *cl, **ll;

    ngx_http_upstream_t  *u;

    u = ctx->request->upstream;

    b = &u->buffer;

    if (u->length == (ssize_t) ctx->rest) {

        if (ngx_strncmp(b->last,

                   ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,

                   bytes)

            != 0)

        {

            ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,

                          "memcached sent invalid trailer");

            u->length = 0;

            ctx->rest = 0;

            return NGX_OK;

        }

        u->length -= bytes;

        ctx->rest -= bytes;

        if (u->length == 0) {

            u->keepalive = 1;

        }

        return NGX_OK;

    }

    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {

        ll = &cl->next;

    }

    cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf->flush = 1;

    cl->buf->memory = 1;

    *ll = cl;

    last = b->last;

    cl->buf->pos = last;

    b->last += bytes;

    cl->buf->last = b->last;

    cl->buf->tag = u->output.tag;

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,

                   "memcached filter bytes:%z size:%z length:%z rest:%z",

                   bytes, b->last - b->pos, u->length, ctx->rest);

    if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {

        u->length -= bytes;

        return NGX_OK;

    }

    last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);

    if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {

        ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,

                      "memcached sent invalid trailer");

        b->last = last;

        cl->buf->last = last;

        u->length = 0;

        ctx->rest = 0;

        return NGX_OK;

    }

    ctx->rest -= b->last - last;

    b->last = last;

    cl->buf->last = last;

    u->length = ctx->rest;

    if (u->length == 0) {

        u->keepalive = 1;

    }

    return NGX_OK;

}

static void

‌ngx_http_memcached_abort_request(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "abort http memcached request");

    return;

}

static void

‌ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http memcached request");

    return;

}

static void *

‌ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_memcached_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->upstream.bufs.num = 0;

     *     conf->upstream.next_upstream = 0;

     *     conf->upstream.temp_path = NULL;

     *     conf->upstream.uri = { 0, NULL };

     *     conf->upstream.location = NULL;

     */

    conf->upstream.local = NGX_CONF_UNSET_PTR;

    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    /* the hardcoded values */

    conf->upstream.cyclic_temp_file = 0;

    conf->upstream.buffering = 0;

    conf->upstream.ignore_client_abort = 0;

    conf->upstream.send_lowat = 0;

    conf->upstream.bufs.num = 0;

    conf->upstream.busy_buffers_size = 0;

    conf->upstream.max_temp_file_size = 0;

    conf->upstream.temp_file_write_size = 0;

    conf->upstream.intercept_errors = 1;

    conf->upstream.intercept_404 = 1;

    conf->upstream.pass_request_headers = 0;

    conf->upstream.pass_request_body = 0;

    conf->index = NGX_CONF_UNSET;

    conf->gzip_flag = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_memcached_loc_conf_t *prev = parent;

    ngx_http_memcached_loc_conf_t *conf = child;

    ngx_conf_merge_ptr_value(conf->upstream.local,

                              prev->upstream.local, NULL);

    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,

                              prev->upstream.next_upstream_tries, 0);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,

                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,

                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,

                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,

                              prev->upstream.next_upstream_timeout, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,

                              prev->upstream.buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,

                              prev->upstream.next_upstream,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_ERROR

                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET

                                       |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (conf->upstream.upstream == NULL) {

        conf->upstream.upstream = prev->upstream.upstream;

    }

    if (conf->index == NGX_CONF_UNSET) {

        conf->index = prev->index;

    }

    ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);

    return NGX_CONF_OK;

}

static char *

‌ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_memcached_loc_conf_t *mlcf = conf;

    ngx_str_t                 *value;

    ngx_url_t                  u;

    ngx_http_core_loc_conf_t  *clcf;

    if (mlcf->upstream.upstream) {

        return "is duplicate";

    }

    value = cf->args->elts;

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.no_resolve = 1;

    mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);

    if (mlcf->upstream.upstream == NULL) {

        return NGX_CONF_ERROR;

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_memcached_handler;

    if (clcf->name.data[clcf->name.len - 1] == '/') {

        clcf->auto_redirect = 1;

    }

    mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);

    if (mlcf->index == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_mp4_module.c - nginx-1.7.9

Global variables defined

ngx_http_mp4_atoms

ngx_http_mp4_commands

ngx_http_mp4_mdia_atoms

ngx_http_mp4_minf_atoms

ngx_http_mp4_module

ngx_http_mp4_module_ctx

ngx_http_mp4_moov_atoms

ngx_http_mp4_stbl_atoms

ngx_http_mp4_trak_atoms

Data types defined

ngx_http_mp4_atom_handler_t

ngx_http_mp4_conf_t

ngx_http_mp4_file_t

ngx_http_mp4_stss_atom_t

ngx_http_mp4_trak_t

ngx_mp4_atom_header64_t

ngx_mp4_atom_header_t

ngx_mp4_co64_atom_t

ngx_mp4_ctts_atom_t

ngx_mp4_ctts_entry_t

ngx_mp4_mdhd64_atom_t

ngx_mp4_mdhd_atom_t

ngx_mp4_mvhd64_atom_t

ngx_mp4_mvhd_atom_t

ngx_mp4_stco_atom_t

ngx_mp4_stsc_atom_t

ngx_mp4_stsc_entry_t

ngx_mp4_stsd_atom_t

ngx_mp4_stsz_atom_t

ngx_mp4_stts_atom_t

ngx_mp4_stts_entry_t

ngx_mp4_tkhd64_atom_t

ngx_mp4_tkhd_atom_t

Functions defined

ngx_http_mp4

ngx_http_mp4_adjust_co64_atom

ngx_http_mp4_adjust_stco_atom

ngx_http_mp4_create_conf

ngx_http_mp4_crop_ctts_data

ngx_http_mp4_crop_stsc_data

ngx_http_mp4_crop_stss_data

ngx_http_mp4_crop_stts_data

ngx_http_mp4_handler

ngx_http_mp4_merge_conf

ngx_http_mp4_process

ngx_http_mp4_read

ngx_http_mp4_read_atom

ngx_http_mp4_read_cmov_atom

ngx_http_mp4_read_co64_atom

ngx_http_mp4_read_ctts_atom

ngx_http_mp4_read_dinf_atom

ngx_http_mp4_read_ftyp_atom

ngx_http_mp4_read_hdlr_atom

ngx_http_mp4_read_mdat_atom

ngx_http_mp4_read_mdhd_atom

ngx_http_mp4_read_mdia_atom

ngx_http_mp4_read_minf_atom

ngx_http_mp4_read_moov_atom

ngx_http_mp4_read_mvhd_atom

ngx_http_mp4_read_smhd_atom

ngx_http_mp4_read_stbl_atom

ngx_http_mp4_read_stco_atom

ngx_http_mp4_read_stsc_atom

ngx_http_mp4_read_stsd_atom

ngx_http_mp4_read_stss_atom

ngx_http_mp4_read_stsz_atom

ngx_http_mp4_read_stts_atom

ngx_http_mp4_read_tkhd_atom

ngx_http_mp4_read_trak_atom

ngx_http_mp4_read_vmhd_atom

ngx_http_mp4_update_co64_atom

ngx_http_mp4_update_ctts_atom

ngx_http_mp4_update_mdat_atom

ngx_http_mp4_update_mdia_atom

ngx_http_mp4_update_minf_atom

ngx_http_mp4_update_stbl_atom

ngx_http_mp4_update_stco_atom

ngx_http_mp4_update_stsc_atom

ngx_http_mp4_update_stss_atom

ngx_http_mp4_update_stsz_atom

ngx_http_mp4_update_stts_atom

ngx_http_mp4_update_trak_atom

Macros defined

NGX_HTTP_MP4_CO64_ATOM

NGX_HTTP_MP4_CO64_DATA

NGX_HTTP_MP4_CTTS_ATOM

NGX_HTTP_MP4_CTTS_DATA

NGX_HTTP_MP4_DINF_ATOM

NGX_HTTP_MP4_HDLR_ATOM

NGX_HTTP_MP4_LAST_ATOM

NGX_HTTP_MP4_MDHD_ATOM

NGX_HTTP_MP4_MDIA_ATOM

NGX_HTTP_MP4_MINF_ATOM

NGX_HTTP_MP4_MOOV_BUFFER_EXCESS

NGX_HTTP_MP4_SMHD_ATOM

NGX_HTTP_MP4_STBL_ATOM

NGX_HTTP_MP4_STCO_ATOM

NGX_HTTP_MP4_STCO_DATA

NGX_HTTP_MP4_STSC_ATOM

NGX_HTTP_MP4_STSC_DATA

NGX_HTTP_MP4_STSC_END

NGX_HTTP_MP4_STSC_START

NGX_HTTP_MP4_STSD_ATOM

NGX_HTTP_MP4_STSS_ATOM

NGX_HTTP_MP4_STSS_DATA

NGX_HTTP_MP4_STSZ_ATOM

NGX_HTTP_MP4_STSZ_DATA

NGX_HTTP_MP4_STTS_ATOM

NGX_HTTP_MP4_STTS_DATA

NGX_HTTP_MP4_TKHD_ATOM

NGX_HTTP_MP4_TRAK_ATOM

NGX_HTTP_MP4_VMHD_ATOM

ngx_mp4_atom_data

ngx_mp4_atom_data_size

ngx_mp4_atom_header

ngx_mp4_atom_next

ngx_mp4_get_32value

ngx_mp4_get_64value

ngx_mp4_last_trak

ngx_mp4_set_32value

ngx_mp4_set_64value

ngx_mp4_set_atom_name

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_MP4_TRAK_ATOM     0

‌#define NGX_HTTP_MP4_TKHD_ATOM     1

‌#define NGX_HTTP_MP4_MDIA_ATOM     2

‌#define NGX_HTTP_MP4_MDHD_ATOM     3

‌#define NGX_HTTP_MP4_HDLR_ATOM     4

‌#define NGX_HTTP_MP4_MINF_ATOM     5

‌#define NGX_HTTP_MP4_VMHD_ATOM     6

‌#define NGX_HTTP_MP4_SMHD_ATOM     7

‌#define NGX_HTTP_MP4_DINF_ATOM     8

‌#define NGX_HTTP_MP4_STBL_ATOM     9

‌#define NGX_HTTP_MP4_STSD_ATOM    10

‌#define NGX_HTTP_MP4_STTS_ATOM    11

‌#define NGX_HTTP_MP4_STTS_DATA    12

‌#define NGX_HTTP_MP4_STSS_ATOM    13

‌#define NGX_HTTP_MP4_STSS_DATA    14

‌#define NGX_HTTP_MP4_CTTS_ATOM    15

‌#define NGX_HTTP_MP4_CTTS_DATA    16

‌#define NGX_HTTP_MP4_STSC_ATOM    17

‌#define NGX_HTTP_MP4_STSC_START   18

‌#define NGX_HTTP_MP4_STSC_DATA    19

‌#define NGX_HTTP_MP4_STSC_END     20

‌#define NGX_HTTP_MP4_STSZ_ATOM    21

‌#define NGX_HTTP_MP4_STSZ_DATA    22

‌#define NGX_HTTP_MP4_STCO_ATOM    23

‌#define NGX_HTTP_MP4_STCO_DATA    24

‌#define NGX_HTTP_MP4_CO64_ATOM    25

‌#define NGX_HTTP_MP4_CO64_DATA    26

‌#define NGX_HTTP_MP4_LAST_ATOM    NGX_HTTP_MP4_CO64_DATA

typedef struct {

    size_t                buffer_size;

    size_t                max_buffer_size;

‌} ngx_http_mp4_conf_t;

typedef struct {

    u_char                chunk[4];

    u_char                samples[4];

    u_char                id[4];

‌} ngx_mp4_stsc_entry_t;

typedef struct {

    uint32_t              timescale;

    uint32_t              time_to_sample_entries;

    uint32_t              sample_to_chunk_entries;

    uint32_t              sync_samples_entries;

    uint32_t              composition_offset_entries;

    uint32_t              sample_sizes_entries;

    uint32_t              chunks;

    ngx_uint_t            start_sample;

    ngx_uint_t            end_sample;

    ngx_uint_t            start_chunk;

    ngx_uint_t            end_chunk;

    ngx_uint_t            start_chunk_samples;

    ngx_uint_t            end_chunk_samples;

    uint64_t              start_chunk_samples_size;

    uint64_t              end_chunk_samples_size;

    off_t                 start_offset;

    off_t                 end_offset;

    size_t                tkhd_size;

    size_t                mdhd_size;

    size_t                hdlr_size;

    size_t                vmhd_size;

    size_t                smhd_size;

    size_t                dinf_size;

    size_t                size;

    ngx_chain_t           out[NGX_HTTP_MP4_LAST_ATOM + 1];

    ngx_buf_t             trak_atom_buf;

    ngx_buf_t             tkhd_atom_buf;

    ngx_buf_t             mdia_atom_buf;

    ngx_buf_t             mdhd_atom_buf;

    ngx_buf_t             hdlr_atom_buf;

    ngx_buf_t             minf_atom_buf;

    ngx_buf_t             vmhd_atom_buf;

    ngx_buf_t             smhd_atom_buf;

    ngx_buf_t             dinf_atom_buf;

    ngx_buf_t             stbl_atom_buf;

    ngx_buf_t             stsd_atom_buf;

    ngx_buf_t             stts_atom_buf;

    ngx_buf_t             stts_data_buf;

    ngx_buf_t             stss_atom_buf;

    ngx_buf_t             stss_data_buf;

    ngx_buf_t             ctts_atom_buf;

    ngx_buf_t             ctts_data_buf;

    ngx_buf_t             stsc_atom_buf;

    ngx_buf_t             stsc_start_chunk_buf;

    ngx_buf_t             stsc_end_chunk_buf;

    ngx_buf_t             stsc_data_buf;

    ngx_buf_t             stsz_atom_buf;

    ngx_buf_t             stsz_data_buf;

    ngx_buf_t             stco_atom_buf;

    ngx_buf_t             stco_data_buf;

    ngx_buf_t             co64_atom_buf;

    ngx_buf_t             co64_data_buf;

    ngx_mp4_stsc_entry_t  stsc_start_chunk_entry;

    ngx_mp4_stsc_entry_t  stsc_end_chunk_entry;

‌} ngx_http_mp4_trak_t;

typedef struct {

    ngx_file_t            file;

    u_char               *buffer;

    u_char               *buffer_start;

    u_char               *buffer_pos;

    u_char               *buffer_end;

    size_t                buffer_size;

    off_t                 offset;

    off_t                 end;

    off_t                 content_length;

    ngx_uint_t            start;

    ngx_uint_t            length;

    uint32_t              timescale;

    ngx_http_request_t   *request;

    ngx_array_t           trak;

    ngx_http_mp4_trak_t   traks[2];

    size_t                ftyp_size;

    size_t                moov_size;

    ngx_chain_t          *out;

    ngx_chain_t           ftyp_atom;

    ngx_chain_t           moov_atom;

    ngx_chain_t           mvhd_atom;

    ngx_chain_t           mdat_atom;

    ngx_chain_t           mdat_data;

    ngx_buf_t             ftyp_atom_buf;

    ngx_buf_t             moov_atom_buf;

    ngx_buf_t             mvhd_atom_buf;

    ngx_buf_t             mdat_atom_buf;

    ngx_buf_t             mdat_data_buf;

    u_char                moov_atom_header[8];

    u_char                mdat_atom_header[16];

‌} ngx_http_mp4_file_t;

typedef struct {

    char                 *name;

    ngx_int_t           (*handler)(ngx_http_mp4_file_t *mp4,

                                   uint64_t atom_data_size);

‌} ngx_http_mp4_atom_handler_t;

‌#define ngx_mp4_atom_header(mp4)   (mp4->buffer_pos - 8)

‌#define ngx_mp4_atom_data(mp4)     mp4->buffer_pos

‌#define ngx_mp4_atom_data_size(t)  (uint64_t) (sizeof(t) - 8)

‌#define ngx_mp4_atom_next(mp4, n)                                             \

    mp4->buffer_pos += (size_t) n;                                            \

    mp4->offset += n

‌#define ngx_mp4_set_atom_name(p, n1, n2, n3, n4)                              \

    ((u_char *) (p))[4] = n1;                                                 \

    ((u_char *) (p))[5] = n2;                                                 \

    ((u_char *) (p))[6] = n3;                                                 \

    ((u_char *) (p))[7] = n4

‌#define ngx_mp4_get_32value(p)                                                \

    ( ((uint32_t) ((u_char *) (p))[0] << 24)                                  \

    + (           ((u_char *) (p))[1] << 16)                                  \

    + (           ((u_char *) (p))[2] << 8)                                   \

    + (           ((u_char *) (p))[3]) )

‌#define ngx_mp4_set_32value(p, n)                                             \

    ((u_char *) (p))[0] = (u_char) ((n) >> 24);                               \

    ((u_char *) (p))[1] = (u_char) ((n) >> 16);                               \

    ((u_char *) (p))[2] = (u_char) ((n) >> 8);                                \

    ((u_char *) (p))[3] = (u_char)  (n)

‌#define ngx_mp4_get_64value(p)                                                \

    ( ((uint64_t) ((u_char *) (p))[0] << 56)                                  \

    + ((uint64_t) ((u_char *) (p))[1] << 48)                                  \

    + ((uint64_t) ((u_char *) (p))[2] << 40)                                  \

    + ((uint64_t) ((u_char *) (p))[3] << 32)                                  \

    + ((uint64_t) ((u_char *) (p))[4] << 24)                                  \

    + (           ((u_char *) (p))[5] << 16)                                  \

    + (           ((u_char *) (p))[6] << 8)                                   \

    + (           ((u_char *) (p))[7]) )

‌#define ngx_mp4_set_64value(p, n)                                             \

    ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56);                    \

    ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48);                    \

    ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40);                    \

    ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32);                    \

    ((u_char *) (p))[4] = (u_char) (           (n) >> 24);                    \

    ((u_char *) (p))[5] = (u_char) (           (n) >> 16);                    \

    ((u_char *) (p))[6] = (u_char) (           (n) >> 8);                     \

    ((u_char *) (p))[7] = (u_char)             (n)

‌#define ngx_mp4_last_trak(mp4)                                                \

    &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]

static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);

static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);

static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,

    off_t start_offset, off_t end_offset);

static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start);

static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start);

static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start);

static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start);

static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, int32_t adjustment);

static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,

    uint64_t atom_data_size);

static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak);

static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, off_t adjustment);

static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);

static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);

‌static ngx_command_t  ngx_http_mp4_commands[] = {

    { ngx_string("mp4"),

      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

      ngx_http_mp4,

      0,

      0,

      NULL },

    { ngx_string("mp4_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_mp4_conf_t, buffer_size),

      NULL },

    { ngx_string("mp4_max_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_mp4_conf_t, max_buffer_size),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_mp4_module_ctx = {

    NULL,                          /* preconfiguration */

    NULL,                          /* postconfiguration */

    NULL,                          /* create main configuration */

    NULL,                          /* init main configuration */

    NULL,                          /* create server configuration */

    NULL,                          /* merge server configuration */

    ngx_http_mp4_create_conf,      /* create location configuration */

    ngx_http_mp4_merge_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_mp4_module = {

    NGX_MODULE_V1,

    &ngx_http_mp4_module_ctx,      /* module context */

    ngx_http_mp4_commands,         /* module directives */

    NGX_HTTP_MODULE,               /* module type */

    NULL,                          /* init master */

    NULL,                          /* init module */

    NULL,                          /* init process */

    NULL,                          /* init thread */

    NULL,                          /* exit thread */

    NULL,                          /* exit process */

    NULL,                          /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_atoms[] = {

    { "ftyp", ngx_http_mp4_read_ftyp_atom },

    { "moov", ngx_http_mp4_read_moov_atom },

    { "mdat", ngx_http_mp4_read_mdat_atom },

    { NULL, NULL }

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_moov_atoms[] = {

    { "mvhd", ngx_http_mp4_read_mvhd_atom },

    { "trak", ngx_http_mp4_read_trak_atom },

    { "cmov", ngx_http_mp4_read_cmov_atom },

    { NULL, NULL }

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_trak_atoms[] = {

    { "tkhd", ngx_http_mp4_read_tkhd_atom },

    { "mdia", ngx_http_mp4_read_mdia_atom },

    { NULL, NULL }

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_mdia_atoms[] = {

    { "mdhd", ngx_http_mp4_read_mdhd_atom },

    { "hdlr", ngx_http_mp4_read_hdlr_atom },

    { "minf", ngx_http_mp4_read_minf_atom },

    { NULL, NULL }

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_minf_atoms[] = {

    { "vmhd", ngx_http_mp4_read_vmhd_atom },

    { "smhd", ngx_http_mp4_read_smhd_atom },

    { "dinf", ngx_http_mp4_read_dinf_atom },

    { "stbl", ngx_http_mp4_read_stbl_atom },

    { NULL, NULL }

};

‌static ngx_http_mp4_atom_handler_t  ngx_http_mp4_stbl_atoms[] = {

    { "stsd", ngx_http_mp4_read_stsd_atom },

    { "stts", ngx_http_mp4_read_stts_atom },

    { "stss", ngx_http_mp4_read_stss_atom },

    { "ctts", ngx_http_mp4_read_ctts_atom },

    { "stsc", ngx_http_mp4_read_stsc_atom },

    { "stsz", ngx_http_mp4_read_stsz_atom },

    { "stco", ngx_http_mp4_read_stco_atom },

    { "co64", ngx_http_mp4_read_co64_atom },

    { NULL, NULL }

};

static ngx_int_t

‌ngx_http_mp4_handler(ngx_http_request_t *r)

{

    u_char                    *last;

    size_t                     root;

    ngx_int_t                  rc, start, end;

    ngx_uint_t                 level, length;

    ngx_str_t                  path, value;

    ngx_log_t                 *log;

    ngx_buf_t                 *b;

    ngx_chain_t                out;

    ngx_http_mp4_file_t       *mp4;

    ngx_open_file_info_t       of;

    ngx_http_core_loc_conf_t  *clcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {

        return NGX_HTTP_NOT_ALLOWED;

    }

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

        return NGX_DECLINED;

    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {

        return rc;

    }

    last = ngx_http_map_uri_to_path(r, &path, &root, 0);

    if (last == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    log = r->connection->log;

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,

                   "http mp4 filename: \"%V\"", &path);

    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 = NGX_MAX_OFF_T_VALUE;

    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;

    }

    if (!of.is_file) {

        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_close_file_n " \"%s\" failed", path.data);

        }

        return NGX_DECLINED;

    }

    r->root_tested = !r->error_page;

    r->allow_ranges = 1;

    start = -1;

    length = 0;

    r->headers_out.content_length_n = of.size;

    mp4 = NULL;

    b = NULL;

    if (r->args.len) {

        if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {

            /*

             * A Flash player may send start value with a lot of digits

             * after dot so strtod() is used instead of atofp().  NaNs and

             * infinities become negative numbers after (int) conversion.

             */

            ngx_set_errno(0);

            start = (int) (strtod((char *) value.data, NULL) * 1000);

            if (ngx_errno != 0) {

                start = -1;

            }

        }

        if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {

            ngx_set_errno(0);

            end = (int) (strtod((char *) value.data, NULL) * 1000);

            if (ngx_errno != 0) {

                end = -1;

            }

            if (end > 0) {

                if (start < 0) {

                    start = 0;

                }

                if (end > start) {

                    length = end - start;

                }

            }

        }

    }

    if (start >= 0) {

        r->single_range = 1;

        mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));

        if (mp4 == NULL) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        mp4->file.fd = of.fd;

        mp4->file.name = path;

        mp4->file.log = r->connection->log;

        mp4->end = of.size;

        mp4->start = (ngx_uint_t) start;

        mp4->length = length;

        mp4->request = r;

        switch (ngx_http_mp4_process(mp4)) {

        case NGX_DECLINED:

            if (mp4->buffer) {

                ngx_pfree(r->pool, mp4->buffer);

            }

            ngx_pfree(r->pool, mp4);

            mp4 = NULL;

            break;

        case NGX_OK:

            r->headers_out.content_length_n = mp4->content_length;

            break;

        default: /* NGX_ERROR */

            if (mp4->buffer) {

                ngx_pfree(r->pool, mp4->buffer);

            }

            ngx_pfree(r->pool, mp4);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

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

    if (clcf->directio <= of.size) {

        /*

         * DIRECTIO is set on transfer only

         * to allow kernel to cache "moov" atom

         */

        if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                          ngx_directio_on_n " \"%s\" failed", path.data);

        }

        of.is_directio = 1;

        if (mp4) {

            mp4->file.directio = 1;

        }

    }

    r->headers_out.status = NGX_HTTP_OK;

    r->headers_out.last_modified_time = of.mtime;

    if (ngx_http_set_etag(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (ngx_http_set_content_type(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    if (mp4 == NULL) {

        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;

    }

    if (mp4) {

        return ngx_http_output_filter(r, mp4->out);

    }

    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);

}

static ngx_int_t

‌ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)

{

    off_t                  start_offset, end_offset, adjustment;

    ngx_int_t              rc;

    ngx_uint_t             i, j;

    ngx_chain_t          **prev;

    ngx_http_mp4_trak_t   *trak;

    ngx_http_mp4_conf_t   *conf;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 start:%ui, length:%ui", mp4->start, mp4->length);

    conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);

    mp4->buffer_size = conf->buffer_size;

    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);

    if (rc != NGX_OK) {

        return rc;

    }

    if (mp4->trak.nelts == 0) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 trak atoms were found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (mp4->mdat_atom.buf == NULL) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 mdat atom was found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    prev = &mp4->out;

    if (mp4->ftyp_atom.buf) {

        *prev = &mp4->ftyp_atom;

        prev = &mp4->ftyp_atom.next;

    }

    *prev = &mp4->moov_atom;

    prev = &mp4->moov_atom.next;

    if (mp4->mvhd_atom.buf) {

        mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;

        *prev = &mp4->mvhd_atom;

        prev = &mp4->mvhd_atom.next;

    }

    start_offset = mp4->end;

    end_offset = 0;

    trak = mp4->trak.elts;

    for (i = 0; i < mp4->trak.nelts; i++) {

        if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {

            return NGX_ERROR;

        }

        ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);

        if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {

            return NGX_ERROR;

        }

        if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {

            return NGX_ERROR;

        }

        if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {

            if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {

                return NGX_ERROR;

            }

        } else {

            if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {

                return NGX_ERROR;

            }

        }

        ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);

        ngx_http_mp4_update_minf_atom(mp4, &trak[i]);

        trak[i].size += trak[i].mdhd_size;

        trak[i].size += trak[i].hdlr_size;

        ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);

        trak[i].size += trak[i].tkhd_size;

        ngx_http_mp4_update_trak_atom(mp4, &trak[i]);

        mp4->moov_size += trak[i].size;

        if (start_offset > trak[i].start_offset) {

            start_offset = trak[i].start_offset;

        }

        if (end_offset < trak[i].end_offset) {

            end_offset = trak[i].end_offset;

        }

        *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];

        prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;

        for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {

            if (trak[i].out[j].buf) {

                *prev = &trak[i].out[j];

                prev = &trak[i].out[j].next;

            }

        }

    }

    if (end_offset < start_offset) {

        end_offset = start_offset;

    }

    mp4->moov_size += 8;

    ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);

    ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');

    mp4->content_length += mp4->moov_size;

    *prev = &mp4->mdat_atom;

    if (start_offset > mp4->mdat_data.buf->file_last) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "start time is out mp4 mdat atom in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    adjustment = mp4->ftyp_size + mp4->moov_size

                 + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)

                 - start_offset;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 adjustment:%O", adjustment);

    for (i = 0; i < mp4->trak.nelts; i++) {

        if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {

            ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);

        } else {

            ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);

        }

    }

    return NGX_OK;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

‌} ngx_mp4_atom_header_t;

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    size64[8];

‌} ngx_mp4_atom_header64_t;

static ngx_int_t

‌ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)

{

    off_t        end;

    size_t       atom_header_size;

    u_char      *atom_header, *atom_name;

    uint64_t     atom_size;

    ngx_int_t    rc;

    ngx_uint_t   n;

    end = mp4->offset + atom_data_size;

    while (mp4->offset < end) {

        if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {

            return NGX_ERROR;

        }

        atom_header = mp4->buffer_pos;

        atom_size = ngx_mp4_get_32value(atom_header);

        atom_header_size = sizeof(ngx_mp4_atom_header_t);

        if (atom_size == 0) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                           "mp4 atom end");

            return NGX_OK;

        }

        if (atom_size < sizeof(ngx_mp4_atom_header_t)) {

            if (atom_size == 1) {

                if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))

                    != NGX_OK)

                {

                    return NGX_ERROR;

                }

                /* 64-bit atom size */

                atom_header = mp4->buffer_pos;

                atom_size = ngx_mp4_get_64value(atom_header + 8);

                atom_header_size = sizeof(ngx_mp4_atom_header64_t);

            } else {

                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                              "\"%s\" mp4 atom is too small:%uL",

                              mp4->file.name.data, atom_size);

                return NGX_ERROR;

            }

        }

        if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {

            return NGX_ERROR;

        }

        atom_header = mp4->buffer_pos;

        atom_name = atom_header + sizeof(uint32_t);

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 atom: %*s @%O:%uL",

                       4, atom_name, mp4->offset, atom_size);

        if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)

            || mp4->offset + (off_t) atom_size > end)

        {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 atom too large:%uL",

                          mp4->file.name.data, atom_size);

            return NGX_ERROR;

        }

        for (n = 0; atom[n].name; n++) {

            if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {

                ngx_mp4_atom_next(mp4, atom_header_size);

                rc = atom[n].handler(mp4, atom_size - atom_header_size);

                if (rc != NGX_OK) {

                    return rc;

                }

                goto next;

            }

        }

        ngx_mp4_atom_next(mp4, atom_size);

    next:

        continue;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)

{

    ssize_t  n;

    if (mp4->buffer_pos + size <= mp4->buffer_end) {

        return NGX_OK;

    }

    if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {

        mp4->buffer_size = (size_t) (mp4->end - mp4->offset);

    }

    if (mp4->buffer_size < size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 file truncated", mp4->file.name.data);

        return NGX_ERROR;

    }

    if (mp4->buffer == NULL) {

        mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);

        if (mp4->buffer == NULL) {

            return NGX_ERROR;

        }

        mp4->buffer_start = mp4->buffer;

    }

    n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,

                      mp4->offset);

    if (n == NGX_ERROR) {

        return NGX_ERROR;

    }

    if ((size_t) n != mp4->buffer_size) {

        ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,

                      ngx_read_file_n " read only %z of %z from \"%s\"",

                      n, mp4->buffer_size, mp4->file.name.data);

        return NGX_ERROR;

    }

    mp4->buffer_pos = mp4->buffer_start;

    mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char     *ftyp_atom;

    size_t      atom_size;

    ngx_buf_t  *atom;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");

    if (atom_data_size > 1024

        || ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 ftyp atom is too large:%uL",

                      mp4->file.name.data, atom_data_size);

        return NGX_ERROR;

    }

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);

    if (ftyp_atom == NULL) {

        return NGX_ERROR;

    }

    ngx_mp4_set_32value(ftyp_atom, atom_size);

    ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');

    /*

     * only moov atom content is guaranteed to be in mp4->buffer

     * during sending response, so ftyp atom content should be copied

     */

    ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),

               ngx_mp4_atom_data(mp4), (size_t) atom_data_size);

    atom = &mp4->ftyp_atom_buf;

    atom->temporary = 1;

    atom->pos = ftyp_atom;

    atom->last = ftyp_atom + atom_size;

    mp4->ftyp_atom.buf = atom;

    mp4->ftyp_size = atom_size;

    mp4->content_length = atom_size;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

/*

* Small excess buffer to process atoms after moov atom, mp4->buffer_start

* will be set to this buffer part after moov atom processing.

*/

‌#define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS  (4 * 1024)

static ngx_int_t

‌ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    ngx_int_t             rc;

    ngx_uint_t            no_mdat;

    ngx_buf_t            *atom;

    ngx_http_mp4_conf_t  *conf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");

    no_mdat = (mp4->mdat_atom.buf == NULL);

    if (no_mdat && mp4->start == 0 && mp4->length == 0) {

        /*

         * send original file if moov atom resides before

         * mdat atom and client requests integral file

         */

        return NGX_DECLINED;

    }

    conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);

    if (atom_data_size > mp4->buffer_size) {

        if (atom_data_size > conf->max_buffer_size) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 moov atom is too large:%uL, "

                          "you may want to increase mp4_max_buffer_size",

                          mp4->file.name.data, atom_data_size);

            return NGX_ERROR;

        }

        ngx_pfree(mp4->request->pool, mp4->buffer);

        mp4->buffer = NULL;

        mp4->buffer_pos = NULL;

        mp4->buffer_end = NULL;

        mp4->buffer_size = (size_t) atom_data_size

                         + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;

    }

    if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {

        return NGX_ERROR;

    }

    mp4->trak.elts = &mp4->traks;

    mp4->trak.size = sizeof(ngx_http_mp4_trak_t);

    mp4->trak.nalloc = 2;

    mp4->trak.pool = mp4->request->pool;

    atom = &mp4->moov_atom_buf;

    atom->temporary = 1;

    atom->pos = mp4->moov_atom_header;

    atom->last = mp4->moov_atom_header + 8;

    mp4->moov_atom.buf = &mp4->moov_atom_buf;

    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");

    if (no_mdat) {

        mp4->buffer_start = mp4->buffer_pos;

        mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;

        if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {

            mp4->buffer = NULL;

            mp4->buffer_pos = NULL;

            mp4->buffer_end = NULL;

        }

    } else {

        /* skip atoms after moov atom */

        mp4->offset = mp4->end;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    ngx_buf_t  *data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");

    data = &mp4->mdat_data_buf;

    data->file = &mp4->file;

    data->in_file = 1;

    data->last_buf = 1;

    data->last_in_chain = 1;

    data->file_last = mp4->offset + atom_data_size;

    mp4->mdat_atom.buf = &mp4->mdat_atom_buf;

    mp4->mdat_atom.next = &mp4->mdat_data;

    mp4->mdat_data.buf = data;

    if (mp4->trak.nelts) {

        /* skip atoms after mdat atom */

        mp4->offset = mp4->end;

    } else {

        ngx_mp4_atom_next(mp4, atom_data_size);

    }

    return NGX_OK;

}

static size_t

‌ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,

    off_t end_offset)

{

    off_t       atom_data_size;

    u_char     *atom_header;

    uint32_t    atom_header_size;

    uint64_t    atom_size;

    ngx_buf_t  *atom;

    atom_data_size = end_offset - start_offset;

    mp4->mdat_data.buf->file_pos = start_offset;

    mp4->mdat_data.buf->file_last = end_offset;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mdat new offset @%O:%O", start_offset, atom_data_size);

    atom_header = mp4->mdat_atom_header;

    if ((uint64_t) atom_data_size > (uint64_t) 0xffffffff) {

        atom_size = 1;

        atom_header_size = sizeof(ngx_mp4_atom_header64_t);

        ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),

                            sizeof(ngx_mp4_atom_header64_t) + atom_data_size);

    } else {

        atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;

        atom_header_size = sizeof(ngx_mp4_atom_header_t);

    }

    mp4->content_length += atom_header_size + atom_data_size;

    ngx_mp4_set_32value(atom_header, atom_size);

    ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');

    atom = &mp4->mdat_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_header_size;

    return atom_header_size;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[4];

    u_char    modification_time[4];

    u_char    timescale[4];

    u_char    duration[4];

    u_char    rate[4];

    u_char    volume[2];

    u_char    reserved[10];

    u_char    matrix[36];

    u_char    preview_time[4];

    u_char    preview_duration[4];

    u_char    poster_time[4];

    u_char    selection_time[4];

    u_char    selection_duration[4];

    u_char    current_time[4];

    u_char    next_track_id[4];

‌} ngx_mp4_mvhd_atom_t;

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[8];

    u_char    modification_time[8];

    u_char    timescale[4];

    u_char    duration[8];

    u_char    rate[4];

    u_char    volume[2];

    u_char    reserved[10];

    u_char    matrix[36];

    u_char    preview_time[4];

    u_char    preview_duration[4];

    u_char    poster_time[4];

    u_char    selection_time[4];

    u_char    selection_duration[4];

    u_char    current_time[4];

    u_char    next_track_id[4];

‌} ngx_mp4_mvhd64_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char                 *atom_header;

    size_t                  atom_size;

    uint32_t                timescale;

    uint64_t                duration, start_time, length_time;

    ngx_buf_t              *atom;

    ngx_mp4_mvhd_atom_t    *mvhd_atom;

    ngx_mp4_mvhd64_atom_t  *mvhd64_atom;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;

    mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;

    ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');

    if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 mvhd atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    if (mvhd_atom->version[0] == 0) {

        /* version 0: 32-bit duration */

        timescale = ngx_mp4_get_32value(mvhd_atom->timescale);

        duration = ngx_mp4_get_32value(mvhd_atom->duration);

    } else {

        /* version 1: 64-bit duration */

        if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 mvhd atom too small",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);

        duration = ngx_mp4_get_64value(mvhd64_atom->duration);

    }

    mp4->timescale = timescale;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mvhd timescale:%uD, duration:%uL, time:%.3fs",

                   timescale, duration, (double) duration / timescale);

    start_time = (uint64_t) mp4->start * timescale / 1000;

    if (duration < start_time) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 start time exceeds file duration",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    duration -= start_time;

    if (mp4->length) {

        length_time = (uint64_t) mp4->length * timescale / 1000;

        if (duration > length_time) {

            duration = length_time;

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mvhd new duration:%uL, time:%.3fs",

                   duration, (double) duration / timescale);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ngx_mp4_set_32value(mvhd_atom->size, atom_size);

    if (mvhd_atom->version[0] == 0) {

        ngx_mp4_set_32value(mvhd_atom->duration, duration);

    } else {

        ngx_mp4_set_64value(mvhd64_atom->duration, duration);

    }

    atom = &mp4->mvhd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    mp4->mvhd_atom.buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_end;

    off_t                 atom_file_end;

    ngx_int_t             rc;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");

    trak = ngx_array_push(&mp4->trak);

    if (trak == NULL) {

        return NGX_ERROR;

    }

    ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));

    atom_header = ngx_mp4_atom_header(mp4);

    ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');

    atom = &trak->trak_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);

    trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;

    atom_end = mp4->buffer_pos + (size_t) atom_data_size;

    atom_file_end = mp4->offset + atom_data_size;

    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 trak atom: %i", rc);

    if (rc == NGX_DECLINED) {

        /* skip this trak */

        ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));

        mp4->trak.nelts--;

        mp4->buffer_pos = atom_end;

        mp4->offset = atom_file_end;

        return NGX_OK;

    }

    return rc;

}

static void

‌ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    ngx_buf_t  *atom;

    trak->size += sizeof(ngx_mp4_atom_header_t);

    atom = &trak->trak_atom_buf;

    ngx_mp4_set_32value(atom->pos, trak->size);

}

static ngx_int_t

‌ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                  "\"%s\" mp4 compressed moov atom (cmov) is not supported",

                  mp4->file.name.data);

    return NGX_ERROR;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[4];

    u_char    modification_time[4];

    u_char    track_id[4];

    u_char    reserved1[4];

    u_char    duration[4];

    u_char    reserved2[8];

    u_char    layer[2];

    u_char    group[2];

    u_char    volume[2];

    u_char    reverved3[2];

    u_char    matrix[36];

    u_char    width[4];

    u_char    heigth[4];

‌} ngx_mp4_tkhd_atom_t;

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[8];

    u_char    modification_time[8];

    u_char    track_id[4];

    u_char    reserved1[4];

    u_char    duration[8];

    u_char    reserved2[8];

    u_char    layer[2];

    u_char    group[2];

    u_char    volume[2];

    u_char    reverved3[2];

    u_char    matrix[36];

    u_char    width[4];

    u_char    heigth[4];

‌} ngx_mp4_tkhd64_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char                 *atom_header;

    size_t                  atom_size;

    uint64_t                duration, start_time, length_time;

    ngx_buf_t              *atom;

    ngx_http_mp4_trak_t    *trak;

    ngx_mp4_tkhd_atom_t    *tkhd_atom;

    ngx_mp4_tkhd64_atom_t  *tkhd64_atom;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;

    tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;

    ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');

    if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 tkhd atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    if (tkhd_atom->version[0] == 0) {

        /* version 0: 32-bit duration */

        duration = ngx_mp4_get_32value(tkhd_atom->duration);

    } else {

        /* version 1: 64-bit duration */

        if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 tkhd atom too small",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        duration = ngx_mp4_get_64value(tkhd64_atom->duration);

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "tkhd duration:%uL, time:%.3fs",

                   duration, (double) duration / mp4->timescale);

    start_time = (uint64_t) mp4->start * mp4->timescale / 1000;

    if (duration <= start_time) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "tkhd duration is less than start time");

        return NGX_DECLINED;

    }

    duration -= start_time;

    if (mp4->length) {

        length_time = (uint64_t) mp4->length * mp4->timescale / 1000;

        if (duration > length_time) {

            duration = length_time;

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "tkhd new duration:%uL, time:%.3fs",

                   duration, (double) duration / mp4->timescale);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    trak = ngx_mp4_last_trak(mp4);

    trak->tkhd_size = atom_size;

    ngx_mp4_set_32value(tkhd_atom->size, atom_size);

    if (tkhd_atom->version[0] == 0) {

        ngx_mp4_set_32value(tkhd_atom->duration, duration);

    } else {

        ngx_mp4_set_64value(tkhd64_atom->duration, duration);

    }

    atom = &trak->tkhd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");

    atom_header = ngx_mp4_atom_header(mp4);

    ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->mdia_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);

    trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;

    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);

}

static void

‌ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    ngx_buf_t  *atom;

    trak->size += sizeof(ngx_mp4_atom_header_t);

    atom = &trak->mdia_atom_buf;

    ngx_mp4_set_32value(atom->pos, trak->size);

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[4];

    u_char    modification_time[4];

    u_char    timescale[4];

    u_char    duration[4];

    u_char    language[2];

    u_char    quality[2];

‌} ngx_mp4_mdhd_atom_t;

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    creation_time[8];

    u_char    modification_time[8];

    u_char    timescale[4];

    u_char    duration[8];

    u_char    language[2];

    u_char    quality[2];

‌} ngx_mp4_mdhd64_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char                 *atom_header;

    size_t                  atom_size;

    uint32_t                timescale;

    uint64_t                duration, start_time, length_time;

    ngx_buf_t              *atom;

    ngx_http_mp4_trak_t    *trak;

    ngx_mp4_mdhd_atom_t    *mdhd_atom;

    ngx_mp4_mdhd64_atom_t  *mdhd64_atom;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;

    mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;

    ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');

    if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 mdhd atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    if (mdhd_atom->version[0] == 0) {

        /* version 0: everything is 32-bit */

        timescale = ngx_mp4_get_32value(mdhd_atom->timescale);

        duration = ngx_mp4_get_32value(mdhd_atom->duration);

    } else {

        /* version 1: 64-bit duration and 32-bit timescale */

        if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 mdhd atom too small",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);

        duration = ngx_mp4_get_64value(mdhd64_atom->duration);

    }

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mdhd timescale:%uD, duration:%uL, time:%.3fs",

                   timescale, duration, (double) duration / timescale);

    start_time = (uint64_t) mp4->start * timescale / 1000;

    if (duration <= start_time) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mdhd duration is less than start time");

        return NGX_DECLINED;

    }

    duration -= start_time;

    if (mp4->length) {

        length_time = (uint64_t) mp4->length * timescale / 1000;

        if (duration > length_time) {

            duration = length_time;

        }

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mdhd new duration:%uL, time:%.3fs",

                   duration, (double) duration / timescale);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    trak = ngx_mp4_last_trak(mp4);

    trak->mdhd_size = atom_size;

    trak->timescale = timescale;

    ngx_mp4_set_32value(mdhd_atom->size, atom_size);

    if (mdhd_atom->version[0] == 0) {

        ngx_mp4_set_32value(mdhd_atom->duration, duration);

    } else {

        ngx_mp4_set_64value(mdhd64_atom->duration, duration);

    }

    atom = &trak->mdhd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char              *atom_header;

    size_t               atom_size;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");

    atom_header = ngx_mp4_atom_header(mp4);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ngx_mp4_set_32value(atom_header, atom_size);

    ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->hdlr_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->hdlr_size = atom_size;

    trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");

    atom_header = ngx_mp4_atom_header(mp4);

    ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->minf_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);

    trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;

    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);

}

static void

‌ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    ngx_buf_t  *atom;

    trak->size += sizeof(ngx_mp4_atom_header_t)

               + trak->vmhd_size

               + trak->smhd_size

               + trak->dinf_size;

    atom = &trak->minf_atom_buf;

    ngx_mp4_set_32value(atom->pos, trak->size);

}

static ngx_int_t

‌ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char              *atom_header;

    size_t               atom_size;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ngx_mp4_set_32value(atom_header, atom_size);

    ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->vmhd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->vmhd_size += atom_size;

    trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char              *atom_header;

    size_t               atom_size;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ngx_mp4_set_32value(atom_header, atom_size);

    ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->smhd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->smhd_size += atom_size;

    trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char              *atom_header;

    size_t               atom_size;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");

    atom_header = ngx_mp4_atom_header(mp4);

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    ngx_mp4_set_32value(atom_header, atom_size);

    ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->dinf_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + atom_size;

    trak->dinf_size += atom_size;

    trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header;

    ngx_buf_t            *atom;

    ngx_http_mp4_trak_t  *trak;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");

    atom_header = ngx_mp4_atom_header(mp4);

    ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->stbl_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);

    trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;

    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);

}

static void

‌ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    ngx_buf_t  *atom;

    trak->size += sizeof(ngx_mp4_atom_header_t);

    atom = &trak->stbl_atom_buf;

    ngx_mp4_set_32value(atom->pos, trak->size);

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

    u_char    media_size[4];

    u_char    media_name[4];

‌} ngx_mp4_stsd_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table;

    size_t                atom_size;

    ngx_buf_t            *atom;

    ngx_mp4_stsd_atom_t  *stsd_atom;

    ngx_http_mp4_trak_t  *trak;

    /* sample description atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;

    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

    atom_table = atom_header + atom_size;

    ngx_mp4_set_32value(stsd_atom->size, atom_size);

    ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');

    if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stsd atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "stsd entries:%uD, media:%*s",

                   ngx_mp4_get_32value(stsd_atom->entries),

                   4, stsd_atom->media_name);

    trak = ngx_mp4_last_trak(mp4);

    atom = &trak->stsd_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;

    trak->size += atom_size;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_mp4_stts_atom_t;

typedef struct {

    u_char    count[4];

    u_char    duration[4];

‌} ngx_mp4_stts_entry_t;

static ngx_int_t

‌ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stts_atom_t  *stts_atom;

    ngx_http_mp4_trak_t  *trak;

    /* time-to-sample atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stts_atom = (ngx_mp4_stts_atom_t *) atom_header;

    ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');

    if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stts atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(stts_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 time-to-sample entries:%uD", entries);

    if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)

        + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stts atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);

    atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);

    trak = ngx_mp4_last_trak(mp4);

    trak->time_to_sample_entries = entries;

    atom = &trak->stts_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    data = &trak->stts_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                atom_size;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stts_atom_t  *stts_atom;

    /*

     * mdia.minf.stbl.stts updating requires trak->timescale

     * from mdia.mdhd atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stts atom update");

    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;

    if (data == NULL) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 stts atoms were found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "time-to-sample entries:%uD", trak->time_to_sample_entries);

    atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;

    stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;

    ngx_mp4_set_32value(stts_atom->size, atom_size);

    ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start)

{

    uint32_t               count, duration, rest;

    uint64_t               start_time;

    ngx_buf_t             *data;

    ngx_uint_t             start_sample, entries, start_sec;

    ngx_mp4_stts_entry_t  *entry, *end;

    if (start) {

        start_sec = mp4->start;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stts crop start_time:%ui", start_sec);

    } else if (mp4->length) {

        start_sec = mp4->length;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stts crop end_time:%ui", start_sec);

    } else {

        return NGX_OK;

    }

    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;

    start_time = (uint64_t) start_sec * trak->timescale / 1000;

    entries = trak->time_to_sample_entries;

    start_sample = 0;

    entry = (ngx_mp4_stts_entry_t *) data->pos;

    end = (ngx_mp4_stts_entry_t *) data->last;

    while (entry < end) {

        count = ngx_mp4_get_32value(entry->count);

        duration = ngx_mp4_get_32value(entry->duration);

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "time:%uL, count:%uD, duration:%uD",

                       start_time, count, duration);

        if (start_time < (uint64_t) count * duration) {

            start_sample += (ngx_uint_t) (start_time / duration);

            rest = (uint32_t) (start_time / duration);

            goto found;

        }

        start_sample += count;

        start_time -= count * duration;

        entries--;

        entry++;

    }

    if (start) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "start time is out mp4 stts samples in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    } else {

        trak->end_sample = trak->start_sample + start_sample;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "end_sample:%ui", trak->end_sample);

        return NGX_OK;

    }

found:

    if (start) {

        ngx_mp4_set_32value(entry->count, count - rest);

        data->pos = (u_char *) entry;

        trak->time_to_sample_entries = entries;

        trak->start_sample = start_sample;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "start_sample:%ui, new count:%uD",

                       trak->start_sample, count - rest);

    } else {

        ngx_mp4_set_32value(entry->count, rest);

        data->last = (u_char *) (entry + 1);

        trak->time_to_sample_entries -= entries - 1;

        trak->end_sample = trak->start_sample + start_sample;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "end_sample:%ui, new count:%uD",

                       trak->end_sample, rest);

    }

    return NGX_OK;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_http_mp4_stss_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char                    *atom_header, *atom_table, *atom_end;

    uint32_t                   entries;

    ngx_buf_t                 *atom, *data;

    ngx_http_mp4_trak_t       *trak;

    ngx_http_mp4_stss_atom_t  *stss_atom;

    /* sync samples atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;

    ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');

    if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stss atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(stss_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sync sample entries:%uD", entries);

    trak = ngx_mp4_last_trak(mp4);

    trak->sync_samples_entries = entries;

    atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);

    atom = &trak->stss_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)

        + entries * sizeof(uint32_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stss atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_end = atom_table + entries * sizeof(uint32_t);

    data = &trak->stss_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                     atom_size;

    uint32_t                   sample, start_sample, *entry, *end;

    ngx_buf_t                 *atom, *data;

    ngx_http_mp4_stss_atom_t  *stss_atom;

    /*

     * mdia.minf.stbl.stss updating requires trak->start_sample

     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stss atom update");

    data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;

    if (data == NULL) {

        return NGX_OK;

    }

    ngx_http_mp4_crop_stss_data(mp4, trak, 1);

    ngx_http_mp4_crop_stss_data(mp4, trak, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sync sample entries:%uD", trak->sync_samples_entries);

    if (trak->sync_samples_entries) {

        entry = (uint32_t *) data->pos;

        end = (uint32_t *) data->last;

        start_sample = trak->start_sample;

        while (entry < end) {

            sample = ngx_mp4_get_32value(entry);

            sample -= start_sample;

            ngx_mp4_set_32value(entry, sample);

            entry++;

        }

    } else {

        trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;

    }

    atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;

    stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;

    ngx_mp4_set_32value(stss_atom->size, atom_size);

    ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);

    return NGX_OK;

}

static void

‌ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start)

{

    uint32_t     sample, start_sample, *entry, *end;

    ngx_buf_t   *data;

    ngx_uint_t   entries;

    /* sync samples starts from 1 */

    if (start) {

        start_sample = trak->start_sample + 1;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stss crop start_sample:%uD", start_sample);

    } else if (mp4->length) {

        start_sample = trak->end_sample + 1;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stss crop end_sample:%uD", start_sample);

    } else {

        return;

    }

    data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;

    entries = trak->sync_samples_entries;

    entry = (uint32_t *) data->pos;

    end = (uint32_t *) data->last;

    while (entry < end) {

        sample = ngx_mp4_get_32value(entry);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "sync:%uD", sample);

        if (sample >= start_sample) {

            goto found;

        }

        entries--;

        entry++;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sample is out of mp4 stss atom");

found:

    if (start) {

        data->pos = (u_char *) entry;

        trak->sync_samples_entries = entries;

    } else {

        data->last = (u_char *) entry;

        trak->sync_samples_entries -= entries;

    }

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_mp4_ctts_atom_t;

typedef struct {

    u_char    count[4];

    u_char    offset[4];

‌} ngx_mp4_ctts_entry_t;

static ngx_int_t

‌ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_ctts_atom_t  *ctts_atom;

    ngx_http_mp4_trak_t  *trak;

    /* composition offsets atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");

    atom_header = ngx_mp4_atom_header(mp4);

    ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;

    ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');

    if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 ctts atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(ctts_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "composition offset entries:%uD", entries);

    trak = ngx_mp4_last_trak(mp4);

    trak->composition_offset_entries = entries;

    atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);

    atom = &trak->ctts_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)

        + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 ctts atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);

    data = &trak->ctts_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static void

‌ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                atom_size;

    ngx_buf_t            *atom, *data;

    ngx_mp4_ctts_atom_t  *ctts_atom;

    /*

     * mdia.minf.stbl.ctts updating requires trak->start_sample

     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 ctts atom update");

    data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;

    if (data == NULL) {

        return;

    }

    ngx_http_mp4_crop_ctts_data(mp4, trak, 1);

    ngx_http_mp4_crop_ctts_data(mp4, trak, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "composition offset entries:%uD",

                   trak->composition_offset_entries);

    if (trak->composition_offset_entries == 0) {

        trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;

        trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;

        return;

    }

    atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;

    ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;

    ngx_mp4_set_32value(ctts_atom->size, atom_size);

    ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);

    return;

}

static void

‌ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start)

{

    uint32_t               count, start_sample, rest;

    ngx_buf_t             *data;

    ngx_uint_t             entries;

    ngx_mp4_ctts_entry_t  *entry, *end;

    /* sync samples starts from 1 */

    if (start) {

        start_sample = trak->start_sample + 1;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 ctts crop start_sample:%uD", start_sample);

    } else if (mp4->length) {

        start_sample = trak->end_sample - trak->start_sample + 1;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 ctts crop end_sample:%uD", start_sample);

    } else {

        return;

    }

    data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;

    entries = trak->composition_offset_entries;

    entry = (ngx_mp4_ctts_entry_t *) data->pos;

    end = (ngx_mp4_ctts_entry_t *) data->last;

    while (entry < end) {

        count = ngx_mp4_get_32value(entry->count);

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "sample:%uD, count:%uD, offset:%uD",

                       start_sample, count, ngx_mp4_get_32value(entry->offset));

         if (start_sample <= count) {

             rest = start_sample - 1;

             goto found;

         }

         start_sample -= count;

         entries--;

         entry++;

    }

    if (start) {

        data->pos = (u_char *) end;

        trak->composition_offset_entries = 0;

    }

    return;

found:

    if (start) {

        ngx_mp4_set_32value(entry->count, count - rest);

        data->pos = (u_char *) entry;

        trak->composition_offset_entries = entries;

    } else {

        ngx_mp4_set_32value(entry->count, rest);

        data->last = (u_char *) (entry + 1);

        trak->composition_offset_entries -= entries - 1;

    }

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_mp4_stsc_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stsc_atom_t  *stsc_atom;

    ngx_http_mp4_trak_t  *trak;

    /* sample-to-chunk atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;

    ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');

    if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stsc atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(stsc_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sample-to-chunk entries:%uD", entries);

    if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)

        + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stsc atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);

    atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);

    trak = ngx_mp4_last_trak(mp4);

    trak->sample_to_chunk_entries = entries;

    atom = &trak->stsc_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    data = &trak->stsc_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                 atom_size;

    uint32_t               chunk;

    ngx_buf_t             *atom, *data;

    ngx_mp4_stsc_atom_t   *stsc_atom;

    ngx_mp4_stsc_entry_t  *entry, *end;

    /*

     * mdia.minf.stbl.stsc updating requires trak->start_sample

     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stsc atom update");

    data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;

    if (data == NULL) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 stsc atoms were found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (trak->sample_to_chunk_entries == 0) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "zero number of entries in stsc atom in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {

        return NGX_ERROR;

    }

    if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sample-to-chunk entries:%uD",

                   trak->sample_to_chunk_entries);

    entry = (ngx_mp4_stsc_entry_t *) data->pos;

    end = (ngx_mp4_stsc_entry_t *) data->last;

    while (entry < end) {

        chunk = ngx_mp4_get_32value(entry->chunk);

        chunk -= trak->start_chunk;

        ngx_mp4_set_32value(entry->chunk, chunk);

        entry++;

    }

    atom_size = sizeof(ngx_mp4_stsc_atom_t)

                + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;

    stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;

    ngx_mp4_set_32value(stsc_atom->size, atom_size);

    ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, ngx_uint_t start)

{

    uint32_t               start_sample, chunk, samples, id, next_chunk, n,

                           prev_samples;

    ngx_buf_t             *data, *buf;

    ngx_uint_t             entries, target_chunk, chunk_samples;

    ngx_mp4_stsc_entry_t  *entry, *end, *first;

    entries = trak->sample_to_chunk_entries - 1;

    if (start) {

        start_sample = (uint32_t) trak->start_sample;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stsc crop start_sample:%uD", start_sample);

    } else if (mp4->length) {

        start_sample = (uint32_t) (trak->end_sample - trak->start_sample);

        samples = 0;

        data = trak->out[NGX_HTTP_MP4_STSC_START].buf;

        if (data) {

            entry = (ngx_mp4_stsc_entry_t *) data->pos;

            samples = ngx_mp4_get_32value(entry->samples);

            entries--;

            if (samples > start_sample) {

                samples = start_sample;

                ngx_mp4_set_32value(entry->samples, samples);

            }

            start_sample -= samples;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "mp4 stsc crop end_sample:%uD, ext_samples:%uD",

                       start_sample, samples);

    } else {

        return NGX_OK;

    }

    data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;

    entry = (ngx_mp4_stsc_entry_t *) data->pos;

    end = (ngx_mp4_stsc_entry_t *) data->last;

    chunk = ngx_mp4_get_32value(entry->chunk);

    samples = ngx_mp4_get_32value(entry->samples);

    id = ngx_mp4_get_32value(entry->id);

    prev_samples = 0;

    entry++;

    while (entry < end) {

        next_chunk = ngx_mp4_get_32value(entry->chunk);

        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "sample:%uD, chunk:%uD, chunks:%uD, "

                       "samples:%uD, id:%uD",

                       start_sample, chunk, next_chunk - chunk, samples, id);

        n = (next_chunk - chunk) * samples;

        if (start_sample < n) {

            goto found;

        }

        start_sample -= n;

        prev_samples = samples;

        chunk = next_chunk;

        samples = ngx_mp4_get_32value(entry->samples);

        id = ngx_mp4_get_32value(entry->id);

        entries--;

        entry++;

    }

    next_chunk = trak->chunks + 1;

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",

                   start_sample, chunk, next_chunk - chunk, samples);

    n = (next_chunk - chunk) * samples;

    if (start_sample > n) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "%s time is out mp4 stsc chunks in \"%s\"",

                      start ? "start" : "end", mp4->file.name.data);

        return NGX_ERROR;

    }

found:

    entries++;

    entry--;

    if (samples == 0) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "zero number of samples in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    target_chunk = chunk - 1;

    target_chunk += start_sample / samples;

    chunk_samples = start_sample % samples;

    if (start) {

        data->pos = (u_char *) entry;

        trak->sample_to_chunk_entries = entries;

        trak->start_chunk = target_chunk;

        trak->start_chunk_samples = chunk_samples;

        ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);

        samples -= chunk_samples;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "start_chunk:%ui, start_chunk_samples:%ui",

                       trak->start_chunk, trak->start_chunk_samples);

    } else {

        if (start_sample) {

            data->last = (u_char *) (entry + 1);

            trak->sample_to_chunk_entries -= entries - 1;

            trak->end_chunk_samples = samples;

        } else {

            data->last = (u_char *) entry;

            trak->sample_to_chunk_entries -= entries;

            trak->end_chunk_samples = prev_samples;

        }

        if (chunk_samples) {

            trak->end_chunk = target_chunk + 1;

            trak->end_chunk_samples = chunk_samples;

        } else {

            trak->end_chunk = target_chunk;

        }

        samples = chunk_samples;

        next_chunk = chunk + 1;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "end_chunk:%ui, end_chunk_samples:%ui",

                       trak->end_chunk, trak->end_chunk_samples);

    }

    if (chunk_samples && next_chunk - target_chunk == 2) {

        ngx_mp4_set_32value(entry->samples, samples);

    } else if (chunk_samples && start) {

        first = &trak->stsc_start_chunk_entry;

        ngx_mp4_set_32value(first->chunk, 1);

        ngx_mp4_set_32value(first->samples, samples);

        ngx_mp4_set_32value(first->id, id);

        buf = &trak->stsc_start_chunk_buf;

        buf->temporary = 1;

        buf->pos = (u_char *) first;

        buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);

        trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;

        ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);

        trak->sample_to_chunk_entries++;

    } else if (chunk_samples) {

        first = &trak->stsc_end_chunk_entry;

        ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);

        ngx_mp4_set_32value(first->samples, samples);

        ngx_mp4_set_32value(first->id, id);

        buf = &trak->stsc_end_chunk_buf;

        buf->temporary = 1;

        buf->pos = (u_char *) first;

        buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);

        trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;

        trak->sample_to_chunk_entries++;

    }

    return NGX_OK;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    uniform_size[4];

    u_char    entries[4];

‌} ngx_mp4_stsz_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    size_t                atom_size;

    uint32_t              entries, size;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stsz_atom_t  *stsz_atom;

    ngx_http_mp4_trak_t  *trak;

    /* sample sizes atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;

    ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');

    if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stsz atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    size = ngx_mp4_get_32value(stsz_atom->uniform_size);

    entries = ngx_mp4_get_32value(stsz_atom->entries);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "sample uniform size:%uD, entries:%uD", size, entries);

    trak = ngx_mp4_last_trak(mp4);

    trak->sample_sizes_entries = entries;

    atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);

    atom = &trak->stsz_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;

    if (size == 0) {

        if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)

            + entries * sizeof(uint32_t) > atom_data_size)

        {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "\"%s\" mp4 stsz atom too small",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        atom_end = atom_table + entries * sizeof(uint32_t);

        data = &trak->stsz_data_buf;

        data->temporary = 1;

        data->pos = atom_table;

        data->last = atom_end;

        trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;

    } else {

        /* if size != 0 then all samples are the same size */

        /* TODO : chunk samples */

        atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;

        ngx_mp4_set_32value(atom_header, atom_size);

        trak->size += atom_size;

    }

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                atom_size;

    uint32_t             *pos, *end, entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stsz_atom_t  *stsz_atom;

    /*

     * mdia.minf.stbl.stsz updating requires trak->start_sample

     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stsz atom update");

    data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;

    if (data) {

        entries = trak->sample_sizes_entries;

        if (trak->start_sample > entries) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "start time is out mp4 stsz samples in \"%s\"",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        entries -= trak->start_sample;

        data->pos += trak->start_sample * sizeof(uint32_t);

        end = (uint32_t *) data->pos;

        for (pos = end - trak->start_chunk_samples; pos < end; pos++) {

            trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                       "chunk samples sizes:%uL",

                       trak->start_chunk_samples_size);

        if (mp4->length) {

            if (trak->end_sample - trak->start_sample > entries) {

                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                              "end time is out mp4 stsz samples in \"%s\"",

                              mp4->file.name.data);

                return NGX_ERROR;

            }

            entries = trak->end_sample - trak->start_sample;

            data->last = data->pos + entries * sizeof(uint32_t);

            end = (uint32_t *) data->last;

            for (pos = end - trak->end_chunk_samples; pos < end; pos++) {

                trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);

            }

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                           "mp4 stsz end_chunk_samples_size:%uL",

                           trak->end_chunk_samples_size);

        }

        atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);

        trak->size += atom_size;

        atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;

        stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;

        ngx_mp4_set_32value(stsz_atom->size, atom_size);

        ngx_mp4_set_32value(stsz_atom->entries, entries);

    }

    return NGX_OK;

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_mp4_stco_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stco_atom_t  *stco_atom;

    ngx_http_mp4_trak_t  *trak;

    /* chunk offsets atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");

    atom_header = ngx_mp4_atom_header(mp4);

    stco_atom = (ngx_mp4_stco_atom_t *) atom_header;

    ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');

    if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stco atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(stco_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);

    if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)

        + entries * sizeof(uint32_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 stco atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);

    atom_end = atom_table + entries * sizeof(uint32_t);

    trak = ngx_mp4_last_trak(mp4);

    trak->chunks = entries;

    atom = &trak->stco_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    data = &trak->stco_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                atom_size;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_stco_atom_t  *stco_atom;

    /*

     * mdia.minf.stbl.stco updating requires trak->start_chunk

     * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stco atom update");

    data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;

    if (data == NULL) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 stco atoms were found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (trak->start_chunk > trak->chunks) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "start time is out mp4 stco chunks in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    data->pos += trak->start_chunk * sizeof(uint32_t);

    trak->start_offset = ngx_mp4_get_32value(data->pos);

    trak->start_offset += trak->start_chunk_samples_size;

    ngx_mp4_set_32value(data->pos, trak->start_offset);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "start chunk offset:%O", trak->start_offset);

    if (mp4->length) {

        if (trak->end_chunk > trak->chunks) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "end time is out mp4 stco chunks in \"%s\"",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        entries = trak->end_chunk - trak->start_chunk;

        data->last = data->pos + entries * sizeof(uint32_t);

        if (entries) {

            trak->end_offset =

                            ngx_mp4_get_32value(data->last - sizeof(uint32_t));

            trak->end_offset += trak->end_chunk_samples_size;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                           "end chunk offset:%O", trak->end_offset);

        }

    } else {

        entries = trak->chunks - trak->start_chunk;

        trak->end_offset = mp4->mdat_data.buf->file_last;

    }

    if (entries == 0) {

        trak->start_offset = mp4->end;

        trak->end_offset = 0;

    }

    atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;

    stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;

    ngx_mp4_set_32value(stco_atom->size, atom_size);

    ngx_mp4_set_32value(stco_atom->entries, entries);

    return NGX_OK;

}

static void

‌ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, int32_t adjustment)

{

    uint32_t    offset, *entry, *end;

    ngx_buf_t  *data;

    /*

     * moov.trak.mdia.minf.stbl.stco adjustment requires

     * minimal start offset of all traks and new moov atom size

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 stco atom adjustment");

    data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;

    entry = (uint32_t *) data->pos;

    end = (uint32_t *) data->last;

    while (entry < end) {

        offset = ngx_mp4_get_32value(entry);

        offset += adjustment;

        ngx_mp4_set_32value(entry, offset);

        entry++;

    }

}

typedef struct {

    u_char    size[4];

    u_char    name[4];

    u_char    version[1];

    u_char    flags[3];

    u_char    entries[4];

‌} ngx_mp4_co64_atom_t;

static ngx_int_t

‌ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)

{

    u_char               *atom_header, *atom_table, *atom_end;

    uint32_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_co64_atom_t  *co64_atom;

    ngx_http_mp4_trak_t  *trak;

    /* chunk offsets atom */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");

    atom_header = ngx_mp4_atom_header(mp4);

    co64_atom = (ngx_mp4_co64_atom_t *) atom_header;

    ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');

    if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 co64 atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    entries = ngx_mp4_get_32value(co64_atom->entries);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);

    if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)

        + entries * sizeof(uint64_t) > atom_data_size)

    {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "\"%s\" mp4 co64 atom too small", mp4->file.name.data);

        return NGX_ERROR;

    }

    atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);

    atom_end = atom_table + entries * sizeof(uint64_t);

    trak = ngx_mp4_last_trak(mp4);

    trak->chunks = entries;

    atom = &trak->co64_atom_buf;

    atom->temporary = 1;

    atom->pos = atom_header;

    atom->last = atom_table;

    data = &trak->co64_data_buf;

    data->temporary = 1;

    data->pos = atom_table;

    data->last = atom_end;

    trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;

    trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;

    ngx_mp4_atom_next(mp4, atom_data_size);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak)

{

    size_t                atom_size;

    uint64_t              entries;

    ngx_buf_t            *atom, *data;

    ngx_mp4_co64_atom_t  *co64_atom;

    /*

     * mdia.minf.stbl.co64 updating requires trak->start_chunk

     * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd

     * atom which may reside after mdia.minf

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 co64 atom update");

    data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;

    if (data == NULL) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "no mp4 co64 atoms were found in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    if (trak->start_chunk > trak->chunks) {

        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                      "start time is out mp4 co64 chunks in \"%s\"",

                      mp4->file.name.data);

        return NGX_ERROR;

    }

    data->pos += trak->start_chunk * sizeof(uint64_t);

    trak->start_offset = ngx_mp4_get_64value(data->pos);

    trak->start_offset += trak->start_chunk_samples_size;

    ngx_mp4_set_64value(data->pos, trak->start_offset);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "start chunk offset:%O", trak->start_offset);

    if (mp4->length) {

        if (trak->end_chunk > trak->chunks) {

            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,

                          "end time is out mp4 co64 chunks in \"%s\"",

                          mp4->file.name.data);

            return NGX_ERROR;

        }

        entries = trak->end_chunk - trak->start_chunk;

        data->last = data->pos + entries * sizeof(uint64_t);

        if (entries) {

            trak->end_offset =

                            ngx_mp4_get_64value(data->last - sizeof(uint64_t));

            trak->end_offset += trak->end_chunk_samples_size;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                           "end chunk offset:%O", trak->end_offset);

        }

    } else {

        entries = trak->chunks - trak->start_chunk;

        trak->end_offset = mp4->mdat_data.buf->file_last;

    }

    if (entries == 0) {

        trak->start_offset = mp4->end;

        trak->end_offset = 0;

    }

    atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);

    trak->size += atom_size;

    atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;

    co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;

    ngx_mp4_set_32value(co64_atom->size, atom_size);

    ngx_mp4_set_32value(co64_atom->entries, entries);

    return NGX_OK;

}

static void

‌ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,

    ngx_http_mp4_trak_t *trak, off_t adjustment)

{

    uint64_t    offset, *entry, *end;

    ngx_buf_t  *data;

    /*

     * moov.trak.mdia.minf.stbl.co64 adjustment requires

     * minimal start offset of all traks and new moov atom size

     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,

                   "mp4 co64 atom adjustment");

    data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;

    entry = (uint64_t *) data->pos;

    end = (uint64_t *) data->last;

    while (entry < end) {

        offset = ngx_mp4_get_64value(entry);

        offset += adjustment;

        ngx_mp4_set_64value(entry, offset);

        entry++;

    }

}

static char *

‌ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_mp4_handler;

    return NGX_CONF_OK;

}

static void *

‌ngx_http_mp4_create_conf(ngx_conf_t *cf)

{

    ngx_http_mp4_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->buffer_size = NGX_CONF_UNSET_SIZE;

    conf->max_buffer_size = NGX_CONF_UNSET_SIZE;

    return conf;

}

static char *

‌ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_mp4_conf_t *prev = parent;

    ngx_http_mp4_conf_t *conf = child;

    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);

    ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,

                              10 * 1024 * 1024);

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_not_modified_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_header_filter

ngx_http_not_modified_filter_module

ngx_http_not_modified_filter_module_ctx

Functions defined

ngx_http_not_modified_filter_init

ngx_http_not_modified_header_filter

ngx_http_test_if_match

ngx_http_test_if_modified

ngx_http_test_if_unmodified

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);

static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);

static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,

    ngx_table_elt_t *header, ngx_uint_t weak);

static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_not_modified_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_not_modified_filter_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 */

};

‌ngx_module_t  ngx_http_not_modified_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_not_modified_filter_module_ctx, /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

static ngx_int_t

‌ngx_http_not_modified_header_filter(ngx_http_request_t *r)

{

    if (r->headers_out.status != NGX_HTTP_OK

        || r != r->main

        || r->disable_not_modified)

    {

        return ngx_http_next_header_filter(r);

    }

    if (r->headers_in.if_unmodified_since

        && !ngx_http_test_if_unmodified(r))

    {

        return ngx_http_filter_finalize_request(r, NULL,

                                                NGX_HTTP_PRECONDITION_FAILED);

    }

    if (r->headers_in.if_match

        && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))

    {

        return ngx_http_filter_finalize_request(r, NULL,

                                                NGX_HTTP_PRECONDITION_FAILED);

    }

    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {

        if (r->headers_in.if_modified_since

            && ngx_http_test_if_modified(r))

        {

            return ngx_http_next_header_filter(r);

        }

        if (r->headers_in.if_none_match

            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))

        {

            return ngx_http_next_header_filter(r);

        }

        /* not modified */

        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;

        r->headers_out.status_line.len = 0;

        r->headers_out.content_type.len = 0;

        ngx_http_clear_content_length(r);

        ngx_http_clear_accept_ranges(r);

        if (r->headers_out.content_encoding) {

            r->headers_out.content_encoding->hash = 0;

            r->headers_out.content_encoding = NULL;

        }

        return ngx_http_next_header_filter(r);

    }

    return ngx_http_next_header_filter(r);

}

static ngx_uint_t

‌ngx_http_test_if_unmodified(ngx_http_request_t *r)

{

    time_t  iums;

    if (r->headers_out.last_modified_time == (time_t) -1) {

        return 0;

    }

    iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,

                               r->headers_in.if_unmodified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                 "http iums:%T lm:%T", iums, r->headers_out.last_modified_time);

    if (iums >= r->headers_out.last_modified_time) {

        return 1;

    }

    return 0;

}

static ngx_uint_t

‌ngx_http_test_if_modified(ngx_http_request_t *r)

{

    time_t                     ims;

    ngx_http_core_loc_conf_t  *clcf;

    if (r->headers_out.last_modified_time == (time_t) -1) {

        return 1;

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {

        return 1;

    }

    ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,

                              r->headers_in.if_modified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http ims:%T lm:%T", ims, r->headers_out.last_modified_time);

    if (ims == r->headers_out.last_modified_time) {

        return 0;

    }

    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT

        || ims < r->headers_out.last_modified_time)

    {

        return 1;

    }

    return 0;

}

static ngx_uint_t

‌ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,

    ngx_uint_t weak)

{

    u_char     *start, *end, ch;

    ngx_str_t   etag, *list;

    list = &header->value;

    if (list->len == 1 && list->data[0] == '*') {

        return 1;

    }

    if (r->headers_out.etag == NULL) {

        return 0;

    }

    etag = r->headers_out.etag->value;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http im:\"%V\" etag:%V", list, &etag);

    if (weak

        && etag.len > 2

        && etag.data[0] == 'W'

        && etag.data[1] == '/')

    {

        etag.len -= 2;

        etag.data += 2;

    }

    start = list->data;

    end = list->data + list->len;

    while (start < end) {

        if (weak

            && end - start > 2

            && start[0] == 'W'

            && start[1] == '/')

        {

            start += 2;

        }

        if (etag.len > (size_t) (end - start)) {

            return 0;

        }

        if (ngx_strncmp(start, etag.data, etag.len) != 0) {

            goto skip;

        }

        start += etag.len;

        while (start < end) {

            ch = *start;

            if (ch == ' ' || ch == '\t') {

                start++;

                continue;

            }

            break;

        }

        if (start == end || *start == ',') {

            return 1;

        }

    skip:

        while (start < end && *start != ',') { start++; }

        while (start < end) {

            ch = *start;

            if (ch == ' ' || ch == '\t' || ch == ',') {

                start++;

                continue;

            }

            break;

        }

    }

    return 0;

}

static ngx_int_t

‌ngx_http_not_modified_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_not_modified_header_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_proxy_module.c - nginx-1.7.9

Global variables defined

ngx_http_proxy_cache_headers

ngx_http_proxy_commands

ngx_http_proxy_headers

ngx_http_proxy_hide_headers

ngx_http_proxy_http_version

ngx_http_proxy_lowat_post

ngx_http_proxy_module

ngx_http_proxy_module

ngx_http_proxy_module_ctx

ngx_http_proxy_next_upstream_masks

ngx_http_proxy_ssl_protocols

ngx_http_proxy_temp_path

ngx_http_proxy_vars

ngx_http_proxy_version

ngx_http_proxy_version_11

Data types defined

ngx_http_proxy_ctx_t

ngx_http_proxy_headers_t

ngx_http_proxy_loc_conf_t

ngx_http_proxy_main_conf_t

ngx_http_proxy_rewrite_pt

ngx_http_proxy_rewrite_s

ngx_http_proxy_rewrite_t

ngx_http_proxy_vars_t

Functions defined

ngx_http_proxy_abort_request

ngx_http_proxy_add_variables

ngx_http_proxy_add_x_forwarded_for_variable

ngx_http_proxy_cache

ngx_http_proxy_cache_key

ngx_http_proxy_chunked_filter

ngx_http_proxy_cookie_domain

ngx_http_proxy_cookie_path

ngx_http_proxy_copy_filter

ngx_http_proxy_create_key

ngx_http_proxy_create_loc_conf

ngx_http_proxy_create_main_conf

ngx_http_proxy_create_request

ngx_http_proxy_eval

ngx_http_proxy_finalize_request

ngx_http_proxy_handler

ngx_http_proxy_host_variable

ngx_http_proxy_init_headers

ngx_http_proxy_input_filter_init

ngx_http_proxy_internal_body_length_variable

ngx_http_proxy_lowat_check

ngx_http_proxy_merge_loc_conf

ngx_http_proxy_non_buffered_chunked_filter

ngx_http_proxy_non_buffered_copy_filter

ngx_http_proxy_pass

ngx_http_proxy_port_variable

ngx_http_proxy_process_header

ngx_http_proxy_process_status_line

ngx_http_proxy_redirect

ngx_http_proxy_reinit_request

ngx_http_proxy_rewrite

ngx_http_proxy_rewrite_complex_handler

ngx_http_proxy_rewrite_cookie

ngx_http_proxy_rewrite_cookie_value

ngx_http_proxy_rewrite_domain_handler

ngx_http_proxy_rewrite_redirect

ngx_http_proxy_rewrite_regex

ngx_http_proxy_rewrite_regex_handler

ngx_http_proxy_set_ssl

ngx_http_proxy_set_vars

ngx_http_proxy_ssl_password_file

ngx_http_proxy_store

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_array_t                    caches;  /* ngx_http_file_cache_t * */

‌} ngx_http_proxy_main_conf_t;

‌typedef struct ngx_http_proxy_rewrite_s  ngx_http_proxy_rewrite_t;

‌typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,

    ngx_table_elt_t *h, size_t prefix, size_t len,

    ngx_http_proxy_rewrite_t *pr);

‌struct ngx_http_proxy_rewrite_s {

    ngx_http_proxy_rewrite_pt      handler;

    union {

        ngx_http_complex_value_t   complex;

#if (NGX_PCRE)

        ngx_http_regex_t          *regex;

#endif

    } pattern;

    ngx_http_complex_value_t       replacement;

};

typedef struct {

    ngx_str_t                      key_start;

    ngx_str_t                      schema;

    ngx_str_t                      host_header;

    ngx_str_t                      port;

    ngx_str_t                      uri;

‌} ngx_http_proxy_vars_t;

typedef struct {

    ngx_array_t                   *flushes;

    ngx_array_t                   *lengths;

    ngx_array_t                   *values;

    ngx_hash_t                     hash;

‌} ngx_http_proxy_headers_t;

typedef struct {

    ngx_http_upstream_conf_t       upstream;

    ngx_array_t                   *body_flushes;

    ngx_array_t                   *body_lengths;

    ngx_array_t                   *body_values;

    ngx_str_t                      body_source;

    ngx_http_proxy_headers_t       headers;

#if (NGX_HTTP_CACHE)

    ngx_http_proxy_headers_t       headers_cache;

#endif

    ngx_array_t                   *headers_source;

    ngx_array_t                   *proxy_lengths;

    ngx_array_t                   *proxy_values;

    ngx_array_t                   *redirects;

    ngx_array_t                   *cookie_domains;

    ngx_array_t                   *cookie_paths;

    ngx_str_t                      method;

    ngx_str_t                      location;

    ngx_str_t                      url;

#if (NGX_HTTP_CACHE)

    ngx_http_complex_value_t       cache_key;

#endif

    ngx_http_proxy_vars_t          vars;

    ngx_flag_t                     redirect;

    ngx_uint_t                     http_version;

    ngx_uint_t                     headers_hash_max_size;

    ngx_uint_t                     headers_hash_bucket_size;

#if (NGX_HTTP_SSL)

    ngx_uint_t                     ssl;

    ngx_uint_t                     ssl_protocols;

    ngx_str_t                      ssl_ciphers;

    ngx_uint_t                     ssl_verify_depth;

    ngx_str_t                      ssl_trusted_certificate;

    ngx_str_t                      ssl_crl;

    ngx_str_t                      ssl_certificate;

    ngx_str_t                      ssl_certificate_key;

    ngx_array_t                   *ssl_passwords;

#endif

‌} ngx_http_proxy_loc_conf_t;

typedef struct {

    ngx_http_status_t              status;

    ngx_http_chunked_t             chunked;

    ngx_http_proxy_vars_t          vars;

    off_t                          internal_body_length;

    ngx_uint_t                     head;  /* unsigned  head:1 */

‌} ngx_http_proxy_ctx_t;

static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,

    ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);

#if (NGX_HTTP_CACHE)

static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);

#endif

static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);

static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);

static ngx_int_t ngx_http_proxy_input_filter_init(void *data);

static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p,

    ngx_buf_t *buf);

static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p,

    ngx_buf_t *buf);

static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,

    ssize_t bytes);

static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,

    ssize_t bytes);

static void ngx_http_proxy_abort_request(ngx_http_request_t *r);

static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,

    ngx_int_t rc);

static ngx_int_t ngx_http_proxy_host_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t

    ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t

    ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,

    ngx_table_elt_t *h, size_t prefix);

static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,

    ngx_table_elt_t *h);

static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,

    ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites);

static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,

    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement);

static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);

static void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_proxy_init_headers(ngx_conf_t *cf,

    ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_headers_t *headers,

    ngx_keyval_t *default_headers);

static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#if (NGX_HTTP_CACHE)

static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

#if (NGX_HTTP_SSL)

static char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,

    ngx_command_t *cmd, void *conf);

#endif

static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);

static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,

    ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);

#if (NGX_HTTP_SSL)

static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,

    ngx_http_proxy_loc_conf_t *plcf);

#endif

static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v);

‌static ngx_conf_post_t  ngx_http_proxy_lowat_post =

    { ngx_http_proxy_lowat_check };

‌static ngx_conf_bitmask_t  ngx_http_proxy_next_upstream_masks[] = {

    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },

    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },

    { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },

    { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },

    { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },

    { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },

    { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },

    { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },

    { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },

    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },

    { ngx_null_string, 0 }

};

#if (NGX_HTTP_SSL)

‌static ngx_conf_bitmask_t  ngx_http_proxy_ssl_protocols[] = {

    { ngx_string("SSLv2"), NGX_SSL_SSLv2 },

    { ngx_string("SSLv3"), NGX_SSL_SSLv3 },

    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },

    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },

    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },

    { ngx_null_string, 0 }

};

#endif

‌static ngx_conf_enum_t  ngx_http_proxy_http_version[] = {

    { ngx_string("1.0"), NGX_HTTP_VERSION_10 },

    { ngx_string("1.1"), NGX_HTTP_VERSION_11 },

    { ngx_null_string, 0 }

};

‌ngx_module_t  ngx_http_proxy_module;

‌static ngx_command_t  ngx_http_proxy_commands[] = {

    { ngx_string("proxy_pass"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,

      ngx_http_proxy_pass,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_redirect"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_proxy_redirect,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_cookie_domain"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_proxy_cookie_domain,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_cookie_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,

      ngx_http_proxy_cookie_path,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_store"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_proxy_store,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_store_access"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_conf_set_access_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access),

      NULL },

    { ngx_string("proxy_buffering"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),

      NULL },

    { ngx_string("proxy_ignore_client_abort"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_client_abort),

      NULL },

    { ngx_string("proxy_bind"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_bind_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.local),

      NULL },

    { ngx_string("proxy_connect_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.connect_timeout),

      NULL },

    { ngx_string("proxy_send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.send_timeout),

      NULL },

    { ngx_string("proxy_send_lowat"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.send_lowat),

      &ngx_http_proxy_lowat_post },

    { ngx_string("proxy_intercept_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors),

      NULL },

    { ngx_string("proxy_set_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_keyval_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, headers_source),

      NULL },

    { ngx_string("proxy_headers_hash_max_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, headers_hash_max_size),

      NULL },

    { ngx_string("proxy_headers_hash_bucket_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, headers_hash_bucket_size),

      NULL },

    { ngx_string("proxy_set_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, body_source),

      NULL },

    { ngx_string("proxy_method"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, method),

      NULL },

    { ngx_string("proxy_pass_request_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),

      NULL },

    { ngx_string("proxy_pass_request_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),

      NULL },

    { ngx_string("proxy_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),

      NULL },

    { ngx_string("proxy_read_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.read_timeout),

      NULL },

    { ngx_string("proxy_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.bufs),

      NULL },

    { ngx_string("proxy_busy_buffers_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf),

      NULL },

    { ngx_string("proxy_force_ranges"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.force_ranges),

      NULL },

    { ngx_string("proxy_limit_rate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.limit_rate),

      NULL },

#if (NGX_HTTP_CACHE)

    { ngx_string("proxy_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_proxy_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_cache_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_proxy_cache_key,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("proxy_cache_path"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,

      ngx_http_file_cache_set_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_proxy_main_conf_t, caches),

      &ngx_http_proxy_module },

    { ngx_string("proxy_cache_bypass"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_bypass),

      NULL },

    { ngx_string("proxy_no_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.no_cache),

      NULL },

    { ngx_string("proxy_cache_valid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_file_cache_valid_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid),

      NULL },

    { ngx_string("proxy_cache_min_uses"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses),

      NULL },

    { ngx_string("proxy_cache_use_stale"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_use_stale),

      &ngx_http_proxy_next_upstream_masks },

    { ngx_string("proxy_cache_methods"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_methods),

      &ngx_http_upstream_cache_method_mask },

    { ngx_string("proxy_cache_lock"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock),

      NULL },

    { ngx_string("proxy_cache_lock_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_timeout),

      NULL },

    { ngx_string("proxy_cache_lock_age"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_age),

      NULL },

    { ngx_string("proxy_cache_revalidate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_revalidate),

      NULL },

#endif

    { ngx_string("proxy_temp_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_conf_set_path_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),

      NULL },

    { ngx_string("proxy_max_temp_file_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.max_temp_file_size_conf),

      NULL },

    { ngx_string("proxy_temp_file_write_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_file_write_size_conf),

      NULL },

    { ngx_string("proxy_next_upstream"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream),

      &ngx_http_proxy_next_upstream_masks },

    { ngx_string("proxy_next_upstream_tries"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_tries),

      NULL },

    { ngx_string("proxy_next_upstream_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_timeout),

      NULL },

    { ngx_string("proxy_pass_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_headers),

      NULL },

    { ngx_string("proxy_hide_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),

      NULL },

    { ngx_string("proxy_ignore_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers),

      &ngx_http_upstream_ignore_headers_masks },

    { ngx_string("proxy_http_version"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_enum_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, http_version),

      &ngx_http_proxy_http_version },

#if (NGX_HTTP_SSL)

    { ngx_string("proxy_ssl_session_reuse"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_session_reuse),

      NULL },

    { ngx_string("proxy_ssl_protocols"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_protocols),

      &ngx_http_proxy_ssl_protocols },

    { ngx_string("proxy_ssl_ciphers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_ciphers),

      NULL },

    { ngx_string("proxy_ssl_name"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_set_complex_value_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_name),

      NULL },

    { ngx_string("proxy_ssl_server_name"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_server_name),

      NULL },

    { ngx_string("proxy_ssl_verify"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify),

      NULL },

    { ngx_string("proxy_ssl_verify_depth"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_verify_depth),

      NULL },

    { ngx_string("proxy_ssl_trusted_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_trusted_certificate),

      NULL },

    { ngx_string("proxy_ssl_crl"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_crl),

      NULL },

    { ngx_string("proxy_ssl_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_certificate),

      NULL },

    { ngx_string("proxy_ssl_certificate_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_proxy_loc_conf_t, ssl_certificate_key),

      NULL },

    { ngx_string("proxy_ssl_password_file"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_proxy_ssl_password_file,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

#endif

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_proxy_module_ctx = {

    ngx_http_proxy_add_variables,          /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_proxy_create_main_conf,       /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_proxy_create_loc_conf,        /* create location configuration */

    ngx_http_proxy_merge_loc_conf          /* merge location configuration */

};

‌ngx_module_t  ngx_http_proxy_module = {

    NGX_MODULE_V1,

    &ngx_http_proxy_module_ctx,            /* module context */

    ngx_http_proxy_commands,               /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static char  ngx_http_proxy_version[] = " HTTP/1.0" CRLF;

‌static char  ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;

‌static ngx_keyval_t  ngx_http_proxy_headers[] = {

    { ngx_string("Host"), ngx_string("$proxy_host") },

    { ngx_string("Connection"), ngx_string("close") },

    { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },

    { ngx_string("TE"), ngx_string("") },

    { ngx_string("Transfer-Encoding"), ngx_string("") },

    { ngx_string("Keep-Alive"), ngx_string("") },

    { ngx_string("Expect"), ngx_string("") },

    { ngx_string("Upgrade"), ngx_string("") },

    { ngx_null_string, ngx_null_string }

};

‌static ngx_str_t  ngx_http_proxy_hide_headers[] = {

    ngx_string("Date"),

    ngx_string("Server"),

    ngx_string("X-Pad"),

    ngx_string("X-Accel-Expires"),

    ngx_string("X-Accel-Redirect"),

    ngx_string("X-Accel-Limit-Rate"),

    ngx_string("X-Accel-Buffering"),

    ngx_string("X-Accel-Charset"),

    ngx_null_string

};

#if (NGX_HTTP_CACHE)

‌static ngx_keyval_t  ngx_http_proxy_cache_headers[] = {

    { ngx_string("Host"), ngx_string("$proxy_host") },

    { ngx_string("Connection"), ngx_string("close") },

    { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },

    { ngx_string("TE"), ngx_string("") },

    { ngx_string("Transfer-Encoding"), ngx_string("") },

    { ngx_string("Keep-Alive"), ngx_string("") },

    { ngx_string("Expect"), ngx_string("") },

    { ngx_string("Upgrade"), ngx_string("") },

    { ngx_string("If-Modified-Since"),

      ngx_string("$upstream_cache_last_modified") },

    { ngx_string("If-Unmodified-Since"), ngx_string("") },

    { ngx_string("If-None-Match"), ngx_string("$upstream_cache_etag") },

    { ngx_string("If-Match"), ngx_string("") },

    { ngx_string("Range"), ngx_string("") },

    { ngx_string("If-Range"), ngx_string("") },

    { ngx_null_string, ngx_null_string }

};

#endif

‌static ngx_http_variable_t  ngx_http_proxy_vars[] = {

    { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0,

      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("proxy_port"), NULL, ngx_http_proxy_port_variable, 0,

      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("proxy_add_x_forwarded_for"), NULL,

      ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },

#if 0

    { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },

#endif

    { ngx_string("proxy_internal_body_length"), NULL,

      ngx_http_proxy_internal_body_length_variable, 0,

      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

‌static ngx_path_init_t  ngx_http_proxy_temp_path = {

    ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }

};

static ngx_int_t

‌ngx_http_proxy_handler(ngx_http_request_t *r)

{

    ngx_int_t                    rc;

    ngx_http_upstream_t         *u;

    ngx_http_proxy_ctx_t        *ctx;

    ngx_http_proxy_loc_conf_t   *plcf;

#if (NGX_HTTP_CACHE)

    ngx_http_proxy_main_conf_t  *pmcf;

#endif

    if (ngx_http_upstream_create(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

    u = r->upstream;

    if (plcf->proxy_lengths == NULL) {

        ctx->vars = plcf->vars;

        u->schema = plcf->vars.schema;

#if (NGX_HTTP_SSL)

        u->ssl = (plcf->upstream.ssl != NULL);

#endif

    } else {

        if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;

    u->conf = &plcf->upstream;

#if (NGX_HTTP_CACHE)

    pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);

    u->caches = &pmcf->caches;

    u->create_key = ngx_http_proxy_create_key;

#endif

    u->create_request = ngx_http_proxy_create_request;

    u->reinit_request = ngx_http_proxy_reinit_request;

    u->process_header = ngx_http_proxy_process_status_line;

    u->abort_request = ngx_http_proxy_abort_request;

    u->finalize_request = ngx_http_proxy_finalize_request;

    r->state = 0;

    if (plcf->redirects) {

        u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;

    }

    if (plcf->cookie_domains || plcf->cookie_paths) {

        u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;

    }

    u->buffering = plcf->upstream.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));

    if (u->pipe == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    u->pipe->input_filter = ngx_http_proxy_copy_filter;

    u->pipe->input_ctx = r;

    u->input_filter_init = ngx_http_proxy_input_filter_init;

    u->input_filter = ngx_http_proxy_non_buffered_copy_filter;

    u->input_filter_ctx = r;

    u->accel = 1;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        return rc;

    }

    return NGX_DONE;

}

static ngx_int_t

‌ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,

    ngx_http_proxy_loc_conf_t *plcf)

{

    u_char               *p;

    size_t                add;

    u_short               port;

    ngx_str_t             proxy;

    ngx_url_t             url;

    ngx_http_upstream_t  *u;

    if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,

                            plcf->proxy_values->elts)

        == NULL)

    {

        return NGX_ERROR;

    }

    if (proxy.len > 7

        && ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)

    {

        add = 7;

        port = 80;

#if (NGX_HTTP_SSL)

    } else if (proxy.len > 8

               && ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)

    {

        add = 8;

        port = 443;

        r->upstream->ssl = 1;

#endif

    } else {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "invalid URL prefix in \"%V\"", &proxy);

        return NGX_ERROR;

    }

    u = r->upstream;

    u->schema.len = add;

    u->schema.data = proxy.data;

    ngx_memzero(&url, sizeof(ngx_url_t));

    url.url.len = proxy.len - add;

    url.url.data = proxy.data + add;

    url.default_port = port;

    url.uri_part = 1;

    url.no_resolve = 1;

    if (ngx_parse_url(r->pool, &url) != NGX_OK) {

        if (url.err) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "%s in upstream \"%V\"", url.err, &url.url);

        }

        return NGX_ERROR;

    }

    if (url.uri.len) {

        if (url.uri.data[0] == '?') {

            p = ngx_pnalloc(r->pool, url.uri.len + 1);

            if (p == NULL) {

                return NGX_ERROR;

            }

            *p++ = '/';

            ngx_memcpy(p, url.uri.data, url.uri.len);

            url.uri.len++;

            url.uri.data = p - 1;

        }

    }

    ctx->vars.key_start = u->schema;

    ngx_http_proxy_set_vars(&url, &ctx->vars);

    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));

    if (u->resolved == NULL) {

        return NGX_ERROR;

    }

    if (url.addrs && url.addrs[0].sockaddr) {

        u->resolved->sockaddr = url.addrs[0].sockaddr;

        u->resolved->socklen = url.addrs[0].socklen;

        u->resolved->naddrs = 1;

        u->resolved->host = url.addrs[0].name;

    } else {

        u->resolved->host = url.host;

        u->resolved->port = (in_port_t) (url.no_port ? port : url.port);

        u->resolved->no_port = url.no_port;

    }

    return NGX_OK;

}

#if (NGX_HTTP_CACHE)

static ngx_int_t

‌ngx_http_proxy_create_key(ngx_http_request_t *r)

{

    size_t                      len, loc_len;

    u_char                     *p;

    uintptr_t                   escape;

    ngx_str_t                  *key;

    ngx_http_upstream_t        *u;

    ngx_http_proxy_ctx_t       *ctx;

    ngx_http_proxy_loc_conf_t  *plcf;

    u = r->upstream;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    key = ngx_array_push(&r->cache->keys);

    if (key == NULL) {

        return NGX_ERROR;

    }

    if (plcf->cache_key.value.data) {

        if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) {

            return NGX_ERROR;

        }

        return NGX_OK;

    }

    *key = ctx->vars.key_start;

    key = ngx_array_push(&r->cache->keys);

    if (key == NULL) {

        return NGX_ERROR;

    }

    if (plcf->proxy_lengths && ctx->vars.uri.len) {

        *key = ctx->vars.uri;

        u->uri = ctx->vars.uri;

        return NGX_OK;

    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)

    {

        *key = r->unparsed_uri;

        u->uri = r->unparsed_uri;

        return NGX_OK;

    }

    loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;

    if (r->quoted_uri || r->internal) {

        escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,

                                    r->uri.len - loc_len, NGX_ESCAPE_URI);

    } else {

        escape = 0;

    }

    len = ctx->vars.uri.len + r->uri.len - loc_len + escape

          + sizeof("?") - 1 + r->args.len;

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    key->data = p;

    if (r->valid_location) {

        p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len);

    }

    if (escape) {

        ngx_escape_uri(p, r->uri.data + loc_len,

                       r->uri.len - loc_len, NGX_ESCAPE_URI);

        p += r->uri.len - loc_len + escape;

    } else {

        p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);

    }

    if (r->args.len > 0) {

        *p++ = '?';

        p = ngx_copy(p, r->args.data, r->args.len);

    }

    key->len = p - key->data;

    u->uri = *key;

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_proxy_create_request(ngx_http_request_t *r)

{

    size_t                        len, uri_len, loc_len, body_len;

    uintptr_t                     escape;

    ngx_buf_t                    *b;

    ngx_str_t                     method;

    ngx_uint_t                    i, unparsed_uri;

    ngx_chain_t                  *cl, *body;

    ngx_list_part_t              *part;

    ngx_table_elt_t              *header;

    ngx_http_upstream_t          *u;

    ngx_http_proxy_ctx_t         *ctx;

    ngx_http_script_code_pt       code;

    ngx_http_proxy_headers_t     *headers;

    ngx_http_script_engine_t      e, le;

    ngx_http_proxy_loc_conf_t    *plcf;

    ngx_http_script_len_code_pt   lcode;

    u = r->upstream;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

#if (NGX_HTTP_CACHE)

    headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;

#else

    headers = &plcf->headers;

#endif

    if (u->method.len) {

        /* HEAD was changed to GET to cache response */

        method = u->method;

        method.len++;

    } else if (plcf->method.len) {

        method = plcf->method;

    } else {

        method = r->method_name;

        method.len++;

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (method.len == 5

        && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0)

    {

        ctx->head = 1;

    }

    len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1;

    escape = 0;

    loc_len = 0;

    unparsed_uri = 0;

    if (plcf->proxy_lengths && ctx->vars.uri.len) {

        uri_len = ctx->vars.uri.len;

    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)

    {

        unparsed_uri = 1;

        uri_len = r->unparsed_uri.len;

    } else {

        loc_len = (r->valid_location && ctx->vars.uri.len) ?

                      plcf->location.len : 0;

        if (r->quoted_uri || r->space_in_uri || r->internal) {

            escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,

                                        r->uri.len - loc_len, NGX_ESCAPE_URI);

        }

        uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape

                  + sizeof("?") - 1 + r->args.len;

    }

    if (uri_len == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "zero length URI to proxy");

        return NGX_ERROR;

    }

    len += uri_len;

    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

    ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);

    ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);

    if (plcf->body_lengths) {

        le.ip = plcf->body_lengths->elts;

        le.request = r;

        le.flushed = 1;

        body_len = 0;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            body_len += lcode(&le);

        }

        ctx->internal_body_length = body_len;

        len += body_len;

    } else {

        ctx->internal_body_length = r->headers_in.content_length_n;

    }

    le.ip = headers->lengths->elts;

    le.request = r;

    le.flushed = 1;

    while (*(uintptr_t *) le.ip) {

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            len += lcode(&le);

        }

        le.ip += sizeof(uintptr_t);

    }

    if (plcf->upstream.pass_request_headers) {

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (ngx_hash_find(&headers->hash, header[i].hash,

                              header[i].lowcase_key, header[i].key.len))

            {

                continue;

            }

            len += header[i].key.len + sizeof(": ") - 1

                + header[i].value.len + sizeof(CRLF) - 1;

        }

    }

    b = ngx_create_temp_buf(r->pool, len);

    if (b == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    /* the request line */

    b->last = ngx_copy(b->last, method.data, method.len);

    u->uri.data = b->last;

    if (plcf->proxy_lengths && ctx->vars.uri.len) {

        b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);

    } else if (unparsed_uri) {

        b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);

    } else {

        if (r->valid_location) {

            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);

        }

        if (escape) {

            ngx_escape_uri(b->last, r->uri.data + loc_len,

                           r->uri.len - loc_len, NGX_ESCAPE_URI);

            b->last += r->uri.len - loc_len + escape;

        } else {

            b->last = ngx_copy(b->last, r->uri.data + loc_len,

                               r->uri.len - loc_len);

        }

        if (r->args.len > 0) {

            *b->last++ = '?';

            b->last = ngx_copy(b->last, r->args.data, r->args.len);

        }

    }

    u->uri.len = b->last - u->uri.data;

    if (plcf->http_version == NGX_HTTP_VERSION_11) {

        b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,

                             sizeof(ngx_http_proxy_version_11) - 1);

    } else {

        b->last = ngx_cpymem(b->last, ngx_http_proxy_version,

                             sizeof(ngx_http_proxy_version) - 1);

    }

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = headers->values->elts;

    e.pos = b->last;

    e.request = r;

    e.flushed = 1;

    le.ip = headers->lengths->elts;

    while (*(uintptr_t *) le.ip) {

        lcode = *(ngx_http_script_len_code_pt *) le.ip;

        /* skip the header line name length */

        (void) lcode(&le);

        if (*(ngx_http_script_len_code_pt *) le.ip) {

            for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0;

        } else {

            e.skip = 0;

        }

        le.ip += sizeof(uintptr_t);

        while (*(uintptr_t *) e.ip) {

            code = *(ngx_http_script_code_pt *) e.ip;

            code((ngx_http_script_engine_t *) &e);

        }

        e.ip += sizeof(uintptr_t);

    }

    b->last = e.pos;

    if (plcf->upstream.pass_request_headers) {

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (ngx_hash_find(&headers->hash, header[i].hash,

                              header[i].lowcase_key, header[i].key.len))

            {

                continue;

            }

            b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);

            *b->last++ = ':'; *b->last++ = ' ';

            b->last = ngx_copy(b->last, header[i].value.data,

                               header[i].value.len);

            *b->last++ = CR; *b->last++ = LF;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http proxy header: \"%V: %V\"",

                           &header[i].key, &header[i].value);

        }

    }

    /* add "\r\n" at the header end */

    *b->last++ = CR; *b->last++ = LF;

    if (plcf->body_values) {

        e.ip = plcf->body_values->elts;

        e.pos = b->last;

        while (*(uintptr_t *) e.ip) {

            code = *(ngx_http_script_code_pt *) e.ip;

            code((ngx_http_script_engine_t *) &e);

        }

        b->last = e.pos;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http proxy header:%N\"%*s\"",

                   (size_t) (b->last - b->pos), b->pos);

    if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {

        body = u->request_bufs;

        u->request_bufs = cl;

        while (body) {

            b = ngx_alloc_buf(r->pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

            cl->next = ngx_alloc_chain_link(r->pool);

            if (cl->next == NULL) {

                return NGX_ERROR;

            }

            cl = cl->next;

            cl->buf = b;

            body = body->next;

        }

    } else {

        u->request_bufs = cl;

    }

    b->flush = 1;

    cl->next = NULL;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_reinit_request(ngx_http_request_t *r)

{

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        return NGX_OK;

    }

    ctx->status.code = 0;

    ctx->status.count = 0;

    ctx->status.start = NULL;

    ctx->status.end = NULL;

    ctx->chunked.state = 0;

    r->upstream->process_header = ngx_http_proxy_process_status_line;

    r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;

    r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter;

    r->state = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_process_status_line(ngx_http_request_t *r)

{

    size_t                 len;

    ngx_int_t              rc;

    ngx_http_upstream_t   *u;

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    u = r->upstream;

    rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);

    if (rc == NGX_AGAIN) {

        return rc;

    }

    if (rc == NGX_ERROR) {

#if (NGX_HTTP_CACHE)

        if (r->cache) {

            r->http_version = NGX_HTTP_VERSION_9;

            return NGX_OK;

        }

#endif

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent no valid HTTP/1.0 header");

#if 0

        if (u->accel) {

            return NGX_HTTP_UPSTREAM_INVALID_HEADER;

        }

#endif

        r->http_version = NGX_HTTP_VERSION_9;

        u->state->status = NGX_HTTP_OK;

        u->headers_in.connection_close = 1;

        return NGX_OK;

    }

    if (u->state && u->state->status == 0) {

        u->state->status = ctx->status.code;

    }

    u->headers_in.status_n = ctx->status.code;

    len = ctx->status.end - ctx->status.start;

    u->headers_in.status_line.len = len;

    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);

    if (u->headers_in.status_line.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http proxy status %ui \"%V\"",

                   u->headers_in.status_n, &u->headers_in.status_line);

    if (ctx->status.http_version < NGX_HTTP_VERSION_11) {

        u->headers_in.connection_close = 1;

    }

    u->process_header = ngx_http_proxy_process_header;

    return ngx_http_proxy_process_header(r);

}

static ngx_int_t

‌ngx_http_proxy_process_header(ngx_http_request_t *r)

{

    ngx_int_t                       rc;

    ngx_table_elt_t                *h;

    ngx_http_upstream_t            *u;

    ngx_http_proxy_ctx_t           *ctx;

    ngx_http_upstream_header_t     *hh;

    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    for ( ;; ) {

        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);

        if (rc == NGX_OK) {

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->upstream->headers_in.headers);

            if (h == NULL) {

                return NGX_ERROR;

            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;

            h->value.len = r->header_end - r->header_start;

            h->key.data = ngx_pnalloc(r->pool,

                               h->key.len + 1 + h->value.len + 1 + h->key.len);

            if (h->key.data == NULL) {

                return NGX_ERROR;

            }

            h->value.data = h->key.data + h->key.len + 1;

            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;

            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);

            h->key.data[h->key.len] = '\0';

            ngx_memcpy(h->value.data, r->header_start, h->value.len);

            h->value.data[h->value.len] = '\0';

            if (h->key.len == r->lowcase_index) {

                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {

                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);

            }

            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,

                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {

                return NGX_ERROR;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http proxy header: \"%V: %V\"",

                           &h->key, &h->value);

            continue;

        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http proxy header done");

            /*

             * if no "Server" and "Date" in header line,

             * then add the special empty headers

             */

            if (r->upstream->headers_in.server == NULL) {

                h = ngx_list_push(&r->upstream->headers_in.headers);

                if (h == NULL) {

                    return NGX_ERROR;

                }

                h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(

                                    ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');

                ngx_str_set(&h->key, "Server");

                ngx_str_null(&h->value);

                h->lowcase_key = (u_char *) "server";

            }

            if (r->upstream->headers_in.date == NULL) {

                h = ngx_list_push(&r->upstream->headers_in.headers);

                if (h == NULL) {

                    return NGX_ERROR;

                }

                h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');

                ngx_str_set(&h->key, "Date");

                ngx_str_null(&h->value);

                h->lowcase_key = (u_char *) "date";

            }

            /* clear content length if response is chunked */

            u = r->upstream;

            if (u->headers_in.chunked) {

                u->headers_in.content_length_n = -1;

            }

            /*

             * set u->keepalive if response has no body; this allows to keep

             * connections alive in case of r->header_only or X-Accel-Redirect

             */

            ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

            if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT

                || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED

                || ctx->head

                || (!u->headers_in.chunked

                    && u->headers_in.content_length_n == 0))

            {

                u->keepalive = !u->headers_in.connection_close;

            }

            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {

                u->keepalive = 0;

                if (r->headers_in.upgrade) {

                    u->upgrade = 1;

                }

            }

            return NGX_OK;

        }

        if (rc == NGX_AGAIN) {

            return NGX_AGAIN;

        }

        /* there was error while a header line parsing */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent invalid header");

        return NGX_HTTP_UPSTREAM_INVALID_HEADER;

    }

}

static ngx_int_t

‌ngx_http_proxy_input_filter_init(void *data)

{

    ngx_http_request_t    *r = data;

    ngx_http_upstream_t   *u;

    ngx_http_proxy_ctx_t  *ctx;

    u = r->upstream;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http proxy filter init s:%d h:%d c:%d l:%O",

                   u->headers_in.status_n, ctx->head, u->headers_in.chunked,

                   u->headers_in.content_length_n);

    /* as per RFC2616, 4.4 Message Length */

    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT

        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED

        || ctx->head)

    {

        /* 1xx, 204, and 304 and replies to HEAD requests */

        /* no 1xx since we don't send Expect and Upgrade */

        u->pipe->length = 0;

        u->length = 0;

        u->keepalive = !u->headers_in.connection_close;

    } else if (u->headers_in.chunked) {

        /* chunked */

        u->pipe->input_filter = ngx_http_proxy_chunked_filter;

        u->pipe->length = 3; /* "0" LF LF */

        u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;

        u->length = 1;

    } else if (u->headers_in.content_length_n == 0) {

        /* empty body: special case as filter won't be called */

        u->pipe->length = 0;

        u->length = 0;

        u->keepalive = !u->headers_in.connection_close;

    } else {

        /* content length or connection close */

        u->pipe->length = u->headers_in.content_length_n;

        u->length = u->headers_in.content_length_n;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)

{

    ngx_buf_t           *b;

    ngx_chain_t         *cl;

    ngx_http_request_t  *r;

    if (buf->pos == buf->last) {

        return NGX_OK;

    }

    cl = ngx_chain_get_free_buf(p->pool, &p->free);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    b = cl->buf;

    ngx_memcpy(b, buf, sizeof(ngx_buf_t));

    b->shadow = buf;

    b->tag = p->tag;

    b->last_shadow = 1;

    b->recycled = 1;

    buf->shadow = b;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);

    if (p->in) {

        *p->last_in = cl;

    } else {

        p->in = cl;

    }

    p->last_in = &cl->next;

    if (p->length == -1) {

        return NGX_OK;

    }

    p->length -= b->last - b->pos;

    if (p->length == 0) {

        r = p->input_ctx;

        p->upstream_done = 1;

        r->upstream->keepalive = !r->upstream->headers_in.connection_close;

    } else if (p->length < 0) {

        r = p->input_ctx;

        p->upstream_done = 1;

        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,

                      "upstream sent more data than specified in "

                      "\"Content-Length\" header");

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)

{

    ngx_int_t              rc;

    ngx_buf_t             *b, **prev;

    ngx_chain_t           *cl;

    ngx_http_request_t    *r;

    ngx_http_proxy_ctx_t  *ctx;

    if (buf->pos == buf->last) {

        return NGX_OK;

    }

    r = p->input_ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    b = NULL;

    prev = &buf->shadow;

    for ( ;; ) {

        rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);

        if (rc == NGX_OK) {

            /* a chunk has been parsed successfully */

            cl = ngx_chain_get_free_buf(p->pool, &p->free);

            if (cl == NULL) {

                return NGX_ERROR;

            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->pos = buf->pos;

            b->start = buf->start;

            b->end = buf->end;

            b->tag = p->tag;

            b->temporary = 1;

            b->recycled = 1;

            *prev = b;

            prev = &b->shadow;

            if (p->in) {

                *p->last_in = cl;

            } else {

                p->in = cl;

            }

            p->last_in = &cl->next;

            /* STUB */ b->num = buf->num;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,

                           "input buf #%d %p", b->num, b->pos);

            if (buf->last - buf->pos >= ctx->chunked.size) {

                buf->pos += (size_t) ctx->chunked.size;

                b->last = buf->pos;

                ctx->chunked.size = 0;

                continue;

            }

            ctx->chunked.size -= buf->last - buf->pos;

            buf->pos = buf->last;

            b->last = buf->last;

            continue;

        }

        if (rc == NGX_DONE) {

            /* a whole response has been parsed successfully */

            p->upstream_done = 1;

            r->upstream->keepalive = !r->upstream->headers_in.connection_close;

            break;

        }

        if (rc == NGX_AGAIN) {

            /* set p->length, minimal amount of data we want to see */

            p->length = ctx->chunked.length;

            break;

        }

        /* invalid response */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent invalid chunked response");

        return NGX_ERROR;

    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http proxy chunked state %d, length %d",

                   ctx->chunked.state, p->length);

    if (b) {

        b->shadow = buf;

        b->last_shadow = 1;

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,

                       "input buf %p %z", b->pos, b->last - b->pos);

        return NGX_OK;

    }

    /* there is no data record in the buf, add it to free chain */

    if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)

{

    ngx_http_request_t   *r = data;

    ngx_buf_t            *b;

    ngx_chain_t          *cl, **ll;

    ngx_http_upstream_t  *u;

    u = r->upstream;

    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {

        ll = &cl->next;

    }

    cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    *ll = cl;

    cl->buf->flush = 1;

    cl->buf->memory = 1;

    b = &u->buffer;

    cl->buf->pos = b->last;

    b->last += bytes;

    cl->buf->last = b->last;

    cl->buf->tag = u->output.tag;

    if (u->length == -1) {

        return NGX_OK;

    }

    u->length -= bytes;

    if (u->length == 0) {

        u->keepalive = !u->headers_in.connection_close;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)

{

    ngx_http_request_t   *r = data;

    ngx_int_t              rc;

    ngx_buf_t             *b, *buf;

    ngx_chain_t           *cl, **ll;

    ngx_http_upstream_t   *u;

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    u = r->upstream;

    buf = &u->buffer;

    buf->pos = buf->last;

    buf->last += bytes;

    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {

        ll = &cl->next;

    }

    for ( ;; ) {

        rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);

        if (rc == NGX_OK) {

            /* a chunk has been parsed successfully */

            cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);

            if (cl == NULL) {

                return NGX_ERROR;

            }

            *ll = cl;

            ll = &cl->next;

            b = cl->buf;

            b->flush = 1;

            b->memory = 1;

            b->pos = buf->pos;

            b->tag = u->output.tag;

            if (buf->last - buf->pos >= ctx->chunked.size) {

                buf->pos += (size_t) ctx->chunked.size;

                b->last = buf->pos;

                ctx->chunked.size = 0;

            } else {

                ctx->chunked.size -= buf->last - buf->pos;

                buf->pos = buf->last;

                b->last = buf->last;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http proxy out buf %p %z",

                           b->pos, b->last - b->pos);

            continue;

        }

        if (rc == NGX_DONE) {

            /* a whole response has been parsed successfully */

            u->keepalive = !u->headers_in.connection_close;

            u->length = 0;

            break;

        }

        if (rc == NGX_AGAIN) {

            break;

        }

        /* invalid response */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent invalid chunked response");

        return NGX_ERROR;

    }

    /* provide continuous buffer for subrequests in memory */

    if (r->subrequest_in_memory) {

        cl = u->out_bufs;

        if (cl) {

            buf->pos = cl->buf->pos;

        }

        buf->last = buf->pos;

        for (cl = u->out_bufs; cl; cl = cl->next) {

            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http proxy in memory %p-%p %uz",

                           cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));

            if (buf->last == cl->buf->pos) {

                buf->last = cl->buf->last;

                continue;

            }

            buf->last = ngx_movemem(buf->last, cl->buf->pos,

                                    cl->buf->last - cl->buf->pos);

            cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);

            cl->buf->last = buf->last;

        }

    }

    return NGX_OK;

}

static void

‌ngx_http_proxy_abort_request(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "abort http proxy request");

    return;

}

static void

‌ngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http proxy request");

    return;

}

static ngx_int_t

‌ngx_http_proxy_host_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->len = ctx->vars.host_header.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = ctx->vars.host_header.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_port_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->len = ctx->vars.port.len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = ctx->vars.port.data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    size_t             len;

    u_char            *p;

    ngx_uint_t         i, n;

    ngx_table_elt_t  **h;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    n = r->headers_in.x_forwarded_for.nelts;

    h = r->headers_in.x_forwarded_for.elts;

    len = 0;

    for (i = 0; i < n; i++) {

        len += h[i]->value.len + sizeof(", ") - 1;

    }

    if (len == 0) {

        v->len = r->connection->addr_text.len;

        v->data = r->connection->addr_text.data;

        return NGX_OK;

    }

    len += r->connection->addr_text.len;

    p = ngx_pnalloc(r->pool, len);

    if (p == NULL) {

        return NGX_ERROR;

    }

    v->len = len;

    v->data = p;

    for (i = 0; i < n; i++) {

        p = ngx_copy(p, h[i]->value.data, h[i]->value.len);

        *p++ = ','; *p++ = ' ';

    }

    ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_proxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (ctx == NULL || ctx->internal_body_length < 0) {

        v->not_found = 1;

        return NGX_OK;

    }

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);

    if (v->data == NULL) {

        return NGX_ERROR;

    }

    v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,

    size_t prefix)

{

    size_t                      len;

    ngx_int_t                   rc;

    ngx_uint_t                  i;

    ngx_http_proxy_rewrite_t   *pr;

    ngx_http_proxy_loc_conf_t  *plcf;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

    pr = plcf->redirects->elts;

    if (pr == NULL) {

        return NGX_DECLINED;

    }

    len = h->value.len - prefix;

    for (i = 0; i < plcf->redirects->nelts; i++) {

        rc = pr[i].handler(r, h, prefix, len, &pr[i]);

        if (rc != NGX_DECLINED) {

            return rc;

        }

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)

{

    size_t                      prefix;

    u_char                     *p;

    ngx_int_t                   rc, rv;

    ngx_http_proxy_loc_conf_t  *plcf;

    p = (u_char *) ngx_strchr(h->value.data, ';');

    if (p == NULL) {

        return NGX_DECLINED;

    }

    prefix = p + 1 - h->value.data;

    rv = NGX_DECLINED;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

    if (plcf->cookie_domains) {

        p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1);

        if (p) {

            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7,

                                                     plcf->cookie_domains);

            if (rc == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (rc != NGX_DECLINED) {

                rv = rc;

            }

        }

    }

    if (plcf->cookie_paths) {

        p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1);

        if (p) {

            rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5,

                                                     plcf->cookie_paths);

            if (rc == NGX_ERROR) {

                return NGX_ERROR;

            }

            if (rc != NGX_DECLINED) {

                rv = rc;

            }

        }

    }

    return rv;

}

static ngx_int_t

‌ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,

    u_char *value, ngx_array_t *rewrites)

{

    size_t                     len, prefix;

    u_char                    *p;

    ngx_int_t                  rc;

    ngx_uint_t                 i;

    ngx_http_proxy_rewrite_t  *pr;

    prefix = value - h->value.data;

    p = (u_char *) ngx_strchr(value, ';');

    len = p ? (size_t) (p - value) : (h->value.len - prefix);

    pr = rewrites->elts;

    for (i = 0; i < rewrites->nelts; i++) {

        rc = pr[i].handler(r, h, prefix, len, &pr[i]);

        if (rc != NGX_DECLINED) {

            return rc;

        }

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,

    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)

{

    ngx_str_t  pattern, replacement;

    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {

        return NGX_ERROR;

    }

    if (pattern.len > len

        || ngx_rstrncmp(h->value.data + prefix, pattern.data,

                        pattern.len) != 0)

    {

        return NGX_DECLINED;

    }

    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {

        return NGX_ERROR;

    }

    return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);

}

#if (NGX_PCRE)

static ngx_int_t

‌ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,

    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)

{

    ngx_str_t  pattern, replacement;

    pattern.len = len;

    pattern.data = h->value.data + prefix;

    if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {

        return NGX_DECLINED;

    }

    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {

        return NGX_ERROR;

    }

    if (prefix == 0 && h->value.len == len) {

        h->value = replacement;

        return NGX_OK;

    }

    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);

}

#endif

static ngx_int_t

‌ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,

    ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)

{

    u_char     *p;

    ngx_str_t   pattern, replacement;

    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {

        return NGX_ERROR;

    }

    p = h->value.data + prefix;

    if (p[0] == '.') {

        p++;

        prefix++;

        len--;

    }

    if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) {

        return NGX_DECLINED;

    }

    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {

        return NGX_ERROR;

    }

    return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);

}

static ngx_int_t

‌ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,

    size_t len, ngx_str_t *replacement)

{

    u_char  *p, *data;

    size_t   new_len;

    new_len = replacement->len + h->value.len - len;

    if (replacement->len > len) {

        data = ngx_pnalloc(r->pool, new_len + 1);

        if (data == NULL) {

            return NGX_ERROR;

        }

        p = ngx_copy(data, h->value.data, prefix);

        p = ngx_copy(p, replacement->data, replacement->len);

        ngx_memcpy(p, h->value.data + prefix + len,

                   h->value.len - len - prefix + 1);

        h->value.data = data;

    } else {

        p = ngx_copy(h->value.data + prefix, replacement->data,

                     replacement->len);

        ngx_memmove(p, h->value.data + prefix + len,

                    h->value.len - len - prefix + 1);

    }

    h->value.len = new_len;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_proxy_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_proxy_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static void *

‌ngx_http_proxy_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_proxy_main_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

#if (NGX_HTTP_CACHE)

    if (ngx_array_init(&conf->caches, cf->pool, 4,

                       sizeof(ngx_http_file_cache_t *))

        != NGX_OK)

    {

        return NULL;

    }

#endif

    return conf;

}

static void *

‌ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_proxy_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->upstream.bufs.num = 0;

     *     conf->upstream.ignore_headers = 0;

     *     conf->upstream.next_upstream = 0;

     *     conf->upstream.cache_zone = NULL;

     *     conf->upstream.cache_use_stale = 0;

     *     conf->upstream.cache_methods = 0;

     *     conf->upstream.temp_path = NULL;

     *     conf->upstream.hide_headers_hash = { NULL, 0 };

     *     conf->upstream.uri = { 0, NULL };

     *     conf->upstream.location = NULL;

     *     conf->upstream.store_lengths = NULL;

     *     conf->upstream.store_values = NULL;

     *     conf->upstream.ssl_name = NULL;

     *

     *     conf->method = { 0, NULL };

     *     conf->headers_source = NULL;

     *     conf->headers.lengths = NULL;

     *     conf->headers.values = NULL;

     *     conf->headers.hash = { NULL, 0 };

     *     conf->headers_cache.lengths = NULL;

     *     conf->headers_cache.values = NULL;

     *     conf->headers_cache.hash = { NULL, 0 };

     *     conf->body_lengths = NULL;

     *     conf->body_values = NULL;

     *     conf->body_source = { 0, NULL };

     *     conf->redirects = NULL;

     *     conf->ssl = 0;

     *     conf->ssl_protocols = 0;

     *     conf->ssl_ciphers = { 0, NULL };

     *     conf->ssl_trusted_certificate = { 0, NULL };

     *     conf->ssl_crl = { 0, NULL };

     *     conf->ssl_certificate = { 0, NULL };

     *     conf->ssl_certificate_key = { 0, NULL };

     */

    conf->upstream.store = NGX_CONF_UNSET;

    conf->upstream.store_access = NGX_CONF_UNSET_UINT;

    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

    conf->upstream.buffering = NGX_CONF_UNSET;

    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;

    conf->upstream.force_ranges = NGX_CONF_UNSET;

    conf->upstream.local = NGX_CONF_UNSET_PTR;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;

    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;

    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.pass_request_headers = NGX_CONF_UNSET;

    conf->upstream.pass_request_body = NGX_CONF_UNSET;

#if (NGX_HTTP_CACHE)

    conf->upstream.cache = NGX_CONF_UNSET;

    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;

    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;

    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_lock = NGX_CONF_UNSET;

    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_revalidate = NGX_CONF_UNSET;

#endif

    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.intercept_errors = NGX_CONF_UNSET;

#if (NGX_HTTP_SSL)

    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;

    conf->upstream.ssl_server_name = NGX_CONF_UNSET;

    conf->upstream.ssl_verify = NGX_CONF_UNSET;

    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;

    conf->ssl_passwords = NGX_CONF_UNSET_PTR;

#endif

    /* "proxy_cyclic_temp_file" is disabled */

    conf->upstream.cyclic_temp_file = 0;

    conf->redirect = NGX_CONF_UNSET;

    conf->upstream.change_buffering = 1;

    conf->cookie_domains = NGX_CONF_UNSET_PTR;

    conf->cookie_paths = NGX_CONF_UNSET_PTR;

    conf->http_version = NGX_CONF_UNSET_UINT;

    conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;

    conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;

    ngx_str_set(&conf->upstream.module, "proxy");

    return conf;

}

static char *

‌ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_proxy_loc_conf_t *prev = parent;

    ngx_http_proxy_loc_conf_t *conf = child;

    u_char                     *p;

    size_t                      size;

    ngx_int_t                   rc;

    ngx_hash_init_t             hash;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_proxy_rewrite_t   *pr;

    ngx_http_script_compile_t   sc;

#if (NGX_HTTP_CACHE)

    if (conf->upstream.store > 0) {

        conf->upstream.cache = 0;

    }

    if (conf->upstream.cache > 0) {

        conf->upstream.store = 0;

    }

#endif

    if (conf->upstream.store == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.store,

                              prev->upstream.store, 0);

        conf->upstream.store_lengths = prev->upstream.store_lengths;

        conf->upstream.store_values = prev->upstream.store_values;

    }

    ngx_conf_merge_uint_value(conf->upstream.store_access,

                              prev->upstream.store_access, 0600);

    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,

                              prev->upstream.next_upstream_tries, 0);

    ngx_conf_merge_value(conf->upstream.buffering,

                              prev->upstream.buffering, 1);

    ngx_conf_merge_value(conf->upstream.ignore_client_abort,

                              prev->upstream.ignore_client_abort, 0);

    ngx_conf_merge_value(conf->upstream.force_ranges,

                              prev->upstream.force_ranges, 0);

    ngx_conf_merge_ptr_value(conf->upstream.local,

                              prev->upstream.local, NULL);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,

                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,

                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,

                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,

                              prev->upstream.next_upstream_timeout, 0);

    ngx_conf_merge_size_value(conf->upstream.send_lowat,

                              prev->upstream.send_lowat, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,

                              prev->upstream.buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_size_value(conf->upstream.limit_rate,

                              prev->upstream.limit_rate, 0);

    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,

                              8, ngx_pagesize);

    if (conf->upstream.bufs.num < 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "there must be at least 2 \"proxy_buffers\"");

        return NGX_CONF_ERROR;

    }

    size = conf->upstream.buffer_size;

    if (size < conf->upstream.bufs.size) {

        size = conf->upstream.bufs.size;

    }

    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,

                              prev->upstream.busy_buffers_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.busy_buffers_size = 2 * size;

    } else {

        conf->upstream.busy_buffers_size =

                                         conf->upstream.busy_buffers_size_conf;

    }

    if (conf->upstream.busy_buffers_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"proxy_busy_buffers_size\" must be equal to or greater than "

             "the maximum of the value of \"proxy_buffer_size\" and "

             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;

    }

    if (conf->upstream.busy_buffers_size

        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"proxy_busy_buffers_size\" must be less than "

             "the size of all \"proxy_buffers\" minus one buffer");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,

                              prev->upstream.temp_file_write_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.temp_file_write_size = 2 * size;

    } else {

        conf->upstream.temp_file_write_size =

                                      conf->upstream.temp_file_write_size_conf;

    }

    if (conf->upstream.temp_file_write_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"proxy_temp_file_write_size\" must be equal to or greater "

             "than the maximum of the value of \"proxy_buffer_size\" and "

             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,

                              prev->upstream.max_temp_file_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;

    } else {

        conf->upstream.max_temp_file_size =

                                        conf->upstream.max_temp_file_size_conf;

    }

    if (conf->upstream.max_temp_file_size != 0

        && conf->upstream.max_temp_file_size < size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

             "\"proxy_max_temp_file_size\" must be equal to zero to disable "

             "temporary files usage or must be equal to or greater than "

             "the maximum of the value of \"proxy_buffer_size\" and "

             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,

                              prev->upstream.ignore_headers,

                              NGX_CONF_BITMASK_SET);

    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,

                              prev->upstream.next_upstream,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_ERROR

                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET

                                       |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,

                              prev->upstream.temp_path,

                              &ngx_http_proxy_temp_path)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.cache,

                              prev->upstream.cache, 0);

        conf->upstream.cache_zone = prev->upstream.cache_zone;

        conf->upstream.cache_value = prev->upstream.cache_value;

    }

    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {

        ngx_shm_zone_t  *shm_zone;

        shm_zone = conf->upstream.cache_zone;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"proxy_cache\" zone \"%V\" is unknown",

                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,

                              prev->upstream.cache_min_uses, 1);

    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,

                              prev->upstream.cache_use_stale,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_OFF));

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET

                                         |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {

        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;

    }

    if (conf->upstream.cache_methods == 0) {

        conf->upstream.cache_methods = prev->upstream.cache_methods;

    }

    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;

    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,

                             prev->upstream.cache_bypass, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.no_cache,

                             prev->upstream.no_cache, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,

                             prev->upstream.cache_valid, NULL);

    if (conf->cache_key.value.data == NULL) {

        conf->cache_key = prev->cache_key;

    }

    ngx_conf_merge_value(conf->upstream.cache_lock,

                              prev->upstream.cache_lock, 0);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,

                              prev->upstream.cache_lock_timeout, 5000);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,

                              prev->upstream.cache_lock_age, 5000);

    ngx_conf_merge_value(conf->upstream.cache_revalidate,

                              prev->upstream.cache_revalidate, 0);

#endif

    ngx_conf_merge_str_value(conf->method, prev->method, "");

    if (conf->method.len

        && conf->method.data[conf->method.len - 1] != ' ')

    {

        conf->method.data[conf->method.len] = ' ';

        conf->method.len++;

    }

    ngx_conf_merge_value(conf->upstream.pass_request_headers,

                              prev->upstream.pass_request_headers, 1);

    ngx_conf_merge_value(conf->upstream.pass_request_body,

                              prev->upstream.pass_request_body, 1);

    ngx_conf_merge_value(conf->upstream.intercept_errors,

                              prev->upstream.intercept_errors, 0);

#if (NGX_HTTP_SSL)

    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,

                              prev->upstream.ssl_session_reuse, 1);

    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,

                                 (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3

                                  |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1

                                  |NGX_SSL_TLSv1_2));

    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,

                             "DEFAULT");

    if (conf->upstream.ssl_name == NULL) {

        conf->upstream.ssl_name = prev->upstream.ssl_name;

    }

    ngx_conf_merge_value(conf->upstream.ssl_server_name,

                              prev->upstream.ssl_server_name, 0);

    ngx_conf_merge_value(conf->upstream.ssl_verify,

                              prev->upstream.ssl_verify, 0);

    ngx_conf_merge_uint_value(conf->ssl_verify_depth,

                              prev->ssl_verify_depth, 1);

    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,

                              prev->ssl_trusted_certificate, "");

    ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");

    ngx_conf_merge_str_value(conf->ssl_certificate,

                              prev->ssl_certificate, "");

    ngx_conf_merge_str_value(conf->ssl_certificate_key,

                              prev->ssl_certificate_key, "");

    ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);

    if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#endif

    ngx_conf_merge_value(conf->redirect, prev->redirect, 1);

    if (conf->redirect) {

        if (conf->redirects == NULL) {

            conf->redirects = prev->redirects;

        }

        if (conf->redirects == NULL && conf->url.data) {

            conf->redirects = ngx_array_create(cf->pool, 1,

                                             sizeof(ngx_http_proxy_rewrite_t));

            if (conf->redirects == NULL) {

                return NGX_CONF_ERROR;

            }

            pr = ngx_array_push(conf->redirects);

            if (pr == NULL) {

                return NGX_CONF_ERROR;

            }

            ngx_memzero(&pr->pattern.complex,

                        sizeof(ngx_http_complex_value_t));

            ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));

            pr->handler = ngx_http_proxy_rewrite_complex_handler;

            if (conf->vars.uri.len) {

                pr->pattern.complex.value = conf->url;

                pr->replacement.value = conf->location;

            } else {

                pr->pattern.complex.value.len = conf->url.len

                                                + sizeof("/") - 1;

                p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);

                if (p == NULL) {

                    return NGX_CONF_ERROR;

                }

                pr->pattern.complex.value.data = p;

                p = ngx_cpymem(p, conf->url.data, conf->url.len);

                *p = '/';

                ngx_str_set(&pr->replacement.value, "/");

            }

        }

    }

    ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL);

    ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);

    ngx_conf_merge_uint_value(conf->http_version, prev->http_version,

                              NGX_HTTP_VERSION_10);

    ngx_conf_merge_uint_value(conf->headers_hash_max_size,

                              prev->headers_hash_max_size, 512);

    ngx_conf_merge_uint_value(conf->headers_hash_bucket_size,

                              prev->headers_hash_bucket_size, 64);

    conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,

                                               ngx_cacheline_size);

    hash.max_size = conf->headers_hash_max_size;

    hash.bucket_size = conf->headers_hash_bucket_size;

    hash.name = "proxy_headers_hash";

    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,

            &prev->upstream, ngx_http_proxy_hide_headers, &hash)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    if (clcf->noname

        && conf->upstream.upstream == NULL && conf->proxy_lengths == NULL)

    {

        conf->upstream.upstream = prev->upstream.upstream;

        conf->location = prev->location;

        conf->vars = prev->vars;

        conf->proxy_lengths = prev->proxy_lengths;

        conf->proxy_values = prev->proxy_values;

#if (NGX_HTTP_SSL)

        conf->upstream.ssl = prev->upstream.ssl;

#endif

    }

    if (clcf->lmt_excpt && clcf->handler == NULL

        && (conf->upstream.upstream || conf->proxy_lengths))

    {

        clcf->handler = ngx_http_proxy_handler;

    }

    if (conf->body_source.data == NULL) {

        conf->body_flushes = prev->body_flushes;

        conf->body_source = prev->body_source;

        conf->body_lengths = prev->body_lengths;

        conf->body_values = prev->body_values;

    }

    if (conf->body_source.data && conf->body_lengths == NULL) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &conf->body_source;

        sc.flushes = &conf->body_flushes;

        sc.lengths = &conf->body_lengths;

        sc.values = &conf->body_values;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    if (conf->headers_source == NULL) {

        conf->headers = prev->headers;

#if (NGX_HTTP_CACHE)

        conf->headers_cache = prev->headers_cache;

#endif

        conf->headers_source = prev->headers_source;

    }

    rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,

                                     ngx_http_proxy_headers);

    if (rc != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache) {

        rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache,

                                         ngx_http_proxy_cache_headers);

        if (rc != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

#endif

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,

    ngx_http_proxy_headers_t *headers, ngx_keyval_t *default_headers)

{

    u_char                       *p;

    size_t                        size;

    uintptr_t                    *code;

    ngx_uint_t                    i;

    ngx_array_t                   headers_names, headers_merged;

    ngx_keyval_t                 *src, *s, *h;

    ngx_hash_key_t               *hk;

    ngx_hash_init_t               hash;

    ngx_http_script_compile_t     sc;

    ngx_http_script_copy_code_t  *copy;

    if (headers->hash.buckets) {

        return NGX_OK;

    }

    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (conf->headers_source == NULL) {

        conf->headers_source = ngx_array_create(cf->pool, 4,

                                                sizeof(ngx_keyval_t));

        if (conf->headers_source == NULL) {

            return NGX_ERROR;

        }

    }

    headers->lengths = ngx_array_create(cf->pool, 64, 1);

    if (headers->lengths == NULL) {

        return NGX_ERROR;

    }

    headers->values = ngx_array_create(cf->pool, 512, 1);

    if (headers->values == NULL) {

        return NGX_ERROR;

    }

    src = conf->headers_source->elts;

    for (i = 0; i < conf->headers_source->nelts; i++) {

        s = ngx_array_push(&headers_merged);

        if (s == NULL) {

            return NGX_ERROR;

        }

        *s = src[i];

    }

    h = default_headers;

    while (h->key.len) {

        src = headers_merged.elts;

        for (i = 0; i < headers_merged.nelts; i++) {

            if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {

                goto next;

            }

        }

        s = ngx_array_push(&headers_merged);

        if (s == NULL) {

            return NGX_ERROR;

        }

        *s = *h;

    next:

        h++;

    }

    src = headers_merged.elts;

    for (i = 0; i < headers_merged.nelts; i++) {

        hk = ngx_array_push(&headers_names);

        if (hk == NULL) {

            return NGX_ERROR;

        }

        hk->key = src[i].key;

        hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);

        hk->value = (void *) 1;

        if (src[i].value.len == 0) {

            continue;

        }

        if (ngx_http_script_variables_count(&src[i].value) == 0) {

            copy = ngx_array_push_n(headers->lengths,

                                    sizeof(ngx_http_script_copy_code_t));

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = (ngx_http_script_code_pt)

                                                 ngx_http_script_copy_len_code;

            copy->len = src[i].key.len + sizeof(": ") - 1

                        + src[i].value.len + sizeof(CRLF) - 1;

            size = (sizeof(ngx_http_script_copy_code_t)

                       + src[i].key.len + sizeof(": ") - 1

                       + src[i].value.len + sizeof(CRLF) - 1

                       + sizeof(uintptr_t) - 1)

                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(headers->values, size);

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = ngx_http_script_copy_code;

            copy->len = src[i].key.len + sizeof(": ") - 1

                        + src[i].value.len + sizeof(CRLF) - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

            p = ngx_cpymem(p, src[i].key.data, src[i].key.len);

            *p++ = ':'; *p++ = ' ';

            p = ngx_cpymem(p, src[i].value.data, src[i].value.len);

            *p++ = CR; *p = LF;

        } else {

            copy = ngx_array_push_n(headers->lengths,

                                    sizeof(ngx_http_script_copy_code_t));

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = (ngx_http_script_code_pt)

                                                 ngx_http_script_copy_len_code;

            copy->len = src[i].key.len + sizeof(": ") - 1;

            size = (sizeof(ngx_http_script_copy_code_t)

                    + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1)

                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(headers->values, size);

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = ngx_http_script_copy_code;

            copy->len = src[i].key.len + sizeof(": ") - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

            p = ngx_cpymem(p, src[i].key.data, src[i].key.len);

            *p++ = ':'; *p = ' ';

            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

            sc.cf = cf;

            sc.source = &src[i].value;

            sc.flushes = &headers->flushes;

            sc.lengths = &headers->lengths;

            sc.values = &headers->values;

            if (ngx_http_script_compile(&sc) != NGX_OK) {

                return NGX_ERROR;

            }

            copy = ngx_array_push_n(headers->lengths,

                                    sizeof(ngx_http_script_copy_code_t));

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = (ngx_http_script_code_pt)

                                                 ngx_http_script_copy_len_code;

            copy->len = sizeof(CRLF) - 1;

            size = (sizeof(ngx_http_script_copy_code_t)

                    + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1)

                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(headers->values, size);

            if (copy == NULL) {

                return NGX_ERROR;

            }

            copy->code = ngx_http_script_copy_code;

            copy->len = sizeof(CRLF) - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

            *p++ = CR; *p = LF;

        }

        code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

        code = ngx_array_push_n(headers->values, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) NULL;

    hash.hash = &headers->hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = conf->headers_hash_max_size;

    hash.bucket_size = conf->headers_hash_bucket_size;

    hash.name = "proxy_headers_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);

}

static char *

‌ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    size_t                      add;

    u_short                     port;

    ngx_str_t                  *value, *url;

    ngx_url_t                   u;

    ngx_uint_t                  n;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_script_compile_t   sc;

    if (plcf->upstream.upstream || plcf->proxy_lengths) {

        return "is duplicate";

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_proxy_handler;

    if (clcf->name.data[clcf->name.len - 1] == '/') {

        clcf->auto_redirect = 1;

    }

    value = cf->args->elts;

    url = &value[1];

    n = ngx_http_script_variables_count(url);

    if (n) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = url;

        sc.lengths = &plcf->proxy_lengths;

        sc.values = &plcf->proxy_values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

#if (NGX_HTTP_SSL)

        plcf->ssl = 1;

#endif

        return NGX_CONF_OK;

    }

    if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) {

        add = 7;

        port = 80;

    } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {

#if (NGX_HTTP_SSL)

        plcf->ssl = 1;

        add = 8;

        port = 443;

#else

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "https protocol requires SSL support");

        return NGX_CONF_ERROR;

#endif

    } else {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");

        return NGX_CONF_ERROR;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url.len = url->len - add;

    u.url.data = url->data + add;

    u.default_port = port;

    u.uri_part = 1;

    u.no_resolve = 1;

    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);

    if (plcf->upstream.upstream == NULL) {

        return NGX_CONF_ERROR;

    }

    plcf->vars.schema.len = add;

    plcf->vars.schema.data = url->data;

    plcf->vars.key_start = plcf->vars.schema;

    ngx_http_proxy_set_vars(&u, &plcf->vars);

    plcf->location = clcf->name;

    if (clcf->named

#if (NGX_PCRE)

        || clcf->regex

#endif

        || clcf->noname)

    {

        if (plcf->vars.uri.len) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"proxy_pass\" cannot have URI part in "

                               "location given by regular expression, "

                               "or inside named location, "

                               "or inside \"if\" statement, "

                               "or inside \"limit_except\" block");

            return NGX_CONF_ERROR;

        }

        plcf->location.len = 0;

    }

    plcf->url = *url;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    u_char                            *p;

    ngx_str_t                         *value;

    ngx_http_proxy_rewrite_t          *pr;

    ngx_http_compile_complex_value_t   ccv;

    if (plcf->redirect == 0) {

        return NGX_CONF_OK;

    }

    plcf->redirect = 1;

    value = cf->args->elts;

    if (cf->args->nelts == 2) {

        if (ngx_strcmp(value[1].data, "off") == 0) {

            plcf->redirect = 0;

            plcf->redirects = NULL;

            return NGX_CONF_OK;

        }

        if (ngx_strcmp(value[1].data, "false") == 0) {

            ngx_conf_log_error(NGX_LOG_ERR, cf, 0,

                           "invalid parameter \"false\", use \"off\" instead");

            plcf->redirect = 0;

            plcf->redirects = NULL;

            return NGX_CONF_OK;

        }

        if (ngx_strcmp(value[1].data, "default") != 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

    }

    if (plcf->redirects == NULL) {

        plcf->redirects = ngx_array_create(cf->pool, 1,

                                           sizeof(ngx_http_proxy_rewrite_t));

        if (plcf->redirects == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    pr = ngx_array_push(plcf->redirects);

    if (pr == NULL) {

        return NGX_CONF_ERROR;

    }

    if (ngx_strcmp(value[1].data, "default") == 0) {

        if (plcf->proxy_lengths) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"proxy_redirect default\" cannot be used "

                               "with \"proxy_pass\" directive with variables");

            return NGX_CONF_ERROR;

        }

        if (plcf->url.data == NULL) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"proxy_redirect default\" should be placed "

                               "after the \"proxy_pass\" directive");

            return NGX_CONF_ERROR;

        }

        pr->handler = ngx_http_proxy_rewrite_complex_handler;

        ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t));

        ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));

        if (plcf->vars.uri.len) {

            pr->pattern.complex.value = plcf->url;

            pr->replacement.value = plcf->location;

        } else {

            pr->pattern.complex.value.len = plcf->url.len + sizeof("/") - 1;

            p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);

            if (p == NULL) {

                return NGX_CONF_ERROR;

            }

            pr->pattern.complex.value.data = p;

            p = ngx_cpymem(p, plcf->url.data, plcf->url.len);

            *p = '/';

            ngx_str_set(&pr->replacement.value, "/");

        }

        return NGX_CONF_OK;

    }

    if (value[1].data[0] == '~') {

        value[1].len--;

        value[1].data++;

        if (value[1].data[0] == '*') {

            value[1].len--;

            value[1].data++;

            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        } else {

            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    } else {

        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

        ccv.cf = cf;

        ccv.value = &value[1];

        ccv.complex_value = &pr->pattern.complex;

        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        pr->handler = ngx_http_proxy_rewrite_complex_handler;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &pr->replacement;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t                         *value;

    ngx_http_proxy_rewrite_t          *pr;

    ngx_http_compile_complex_value_t   ccv;

    if (plcf->cookie_domains == NULL) {

        return NGX_CONF_OK;

    }

    value = cf->args->elts;

    if (cf->args->nelts == 2) {

        if (ngx_strcmp(value[1].data, "off") == 0) {

            plcf->cookie_domains = NULL;

            return NGX_CONF_OK;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) {

        plcf->cookie_domains = ngx_array_create(cf->pool, 1,

                                     sizeof(ngx_http_proxy_rewrite_t));

        if (plcf->cookie_domains == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    pr = ngx_array_push(plcf->cookie_domains);

    if (pr == NULL) {

        return NGX_CONF_ERROR;

    }

    if (value[1].data[0] == '~') {

        value[1].len--;

        value[1].data++;

        if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    } else {

        if (value[1].data[0] == '.') {

            value[1].len--;

            value[1].data++;

        }

        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

        ccv.cf = cf;

        ccv.value = &value[1];

        ccv.complex_value = &pr->pattern.complex;

        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        pr->handler = ngx_http_proxy_rewrite_domain_handler;

        if (value[2].data[0] == '.') {

            value[2].len--;

            value[2].data++;

        }

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &pr->replacement;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t                         *value;

    ngx_http_proxy_rewrite_t          *pr;

    ngx_http_compile_complex_value_t   ccv;

    if (plcf->cookie_paths == NULL) {

        return NGX_CONF_OK;

    }

    value = cf->args->elts;

    if (cf->args->nelts == 2) {

        if (ngx_strcmp(value[1].data, "off") == 0) {

            plcf->cookie_paths = NULL;

            return NGX_CONF_OK;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) {

        plcf->cookie_paths = ngx_array_create(cf->pool, 1,

                                     sizeof(ngx_http_proxy_rewrite_t));

        if (plcf->cookie_paths == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    pr = ngx_array_push(plcf->cookie_paths);

    if (pr == NULL) {

        return NGX_CONF_ERROR;

    }

    if (value[1].data[0] == '~') {

        value[1].len--;

        value[1].data++;

        if (value[1].data[0] == '*') {

            value[1].len--;

            value[1].data++;

            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        } else {

            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    } else {

        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

        ccv.cf = cf;

        ccv.value = &value[1];

        ccv.complex_value = &pr->pattern.complex;

        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        pr->handler = ngx_http_proxy_rewrite_complex_handler;

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &pr->replacement;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,

    ngx_str_t *regex, ngx_uint_t caseless)

{

#if (NGX_PCRE)

    u_char               errstr[NGX_MAX_CONF_ERRSTR];

    ngx_regex_compile_t  rc;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pattern = *regex;

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

    if (caseless) {

        rc.options = NGX_REGEX_CASELESS;

    }

    pr->pattern.regex = ngx_http_regex_compile(cf, &rc);

    if (pr->pattern.regex == NULL) {

        return NGX_ERROR;

    }

    pr->handler = ngx_http_proxy_rewrite_regex_handler;

    return NGX_OK;

#else

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "using regex \"%V\" requires PCRE library", regex);

    return NGX_ERROR;

#endif

}

static char *

‌ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t                  *value;

    ngx_http_script_compile_t   sc;

    if (plcf->upstream.store != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        plcf->upstream.store = 0;

        return NGX_CONF_OK;

    }

#if (NGX_HTTP_CACHE)

    if (plcf->upstream.cache > 0) {

        return "is incompatible with \"proxy_cache\"";

    }

#endif

    plcf->upstream.store = 1;

    if (ngx_strcmp(value[1].data, "on") == 0) {

        return NGX_CONF_OK;

    }

    /* include the terminating '\0' into script */

    value[1].len++;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = &value[1];

    sc.lengths = &plcf->upstream.store_lengths;

    sc.values = &plcf->upstream.store_values;

    sc.variables = ngx_http_script_variables_count(&value[1]);

    sc.complete_lengths = 1;

    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#if (NGX_HTTP_CACHE)

static char *

‌ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t                         *value;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (plcf->upstream.cache != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        plcf->upstream.cache = 0;

        return NGX_CONF_OK;

    }

    if (plcf->upstream.store > 0) {

        return "is incompatible with \"proxy_store\"";

    }

    plcf->upstream.cache = 1;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths != NULL) {

        plcf->upstream.cache_value = ngx_palloc(cf->pool,

                                             sizeof(ngx_http_complex_value_t));

        if (plcf->upstream.cache_value == NULL) {

            return NGX_CONF_ERROR;

        }

        *plcf->upstream.cache_value = cv;

        return NGX_CONF_OK;

    }

    plcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,

                                                      &ngx_http_proxy_module);

    if (plcf->upstream.cache_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (plcf->cache_key.value.data) {

        return "is duplicate";

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &plcf->cache_key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

#if (NGX_HTTP_SSL)

static char *

‌ngx_http_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_proxy_loc_conf_t *plcf = conf;

    ngx_str_t  *value;

    if (plcf->ssl_passwords != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    plcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);

    if (plcf->ssl_passwords == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

static char *

‌ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)

{

#if (NGX_FREEBSD)

    ssize_t *np = data;

    if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"proxy_send_lowat\" must be less than %d "

                           "(sysctl net.inet.tcp.sendspace)",

                           ngx_freebsd_net_inet_tcp_sendspace);

        return NGX_CONF_ERROR;

    }

#elif !(NGX_HAVE_SO_SNDLOWAT)

    ssize_t *np = data;

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                       "\"proxy_send_lowat\" is not supported, ignored");

    *np = 0;

#endif

    return NGX_CONF_OK;

}

#if (NGX_HTTP_SSL)

static ngx_int_t

‌ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)

{

    ngx_pool_cleanup_t  *cln;

    plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));

    if (plcf->upstream.ssl == NULL) {

        return NGX_ERROR;

    }

    plcf->upstream.ssl->log = cf->log;

    if (ngx_ssl_create(plcf->upstream.ssl, plcf->ssl_protocols, NULL)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_ERROR;

    }

    cln->handler = ngx_ssl_cleanup_ctx;

    cln->data = plcf->upstream.ssl;

    if (plcf->ssl_certificate.len) {

        if (plcf->ssl_certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"proxy_ssl_certificate_key\" is defined "

                          "for certificate \"%V\"", &plcf->ssl_certificate);

            return NGX_ERROR;

        }

        if (ngx_ssl_certificate(cf, plcf->upstream.ssl, &plcf->ssl_certificate,

                                &plcf->ssl_certificate_key, plcf->ssl_passwords)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    if (SSL_CTX_set_cipher_list(plcf->upstream.ssl->ctx,

                                (const char *) plcf->ssl_ciphers.data)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,

                      "SSL_CTX_set_cipher_list(\"%V\") failed",

                      &plcf->ssl_ciphers);

        return NGX_ERROR;

    }

    if (plcf->upstream.ssl_verify) {

        if (plcf->ssl_trusted_certificate.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "no proxy_ssl_trusted_certificate for proxy_ssl_verify");

            return NGX_ERROR;

        }

        if (ngx_ssl_trusted_certificate(cf, plcf->upstream.ssl,

                                        &plcf->ssl_trusted_certificate,

                                        plcf->ssl_verify_depth)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        if (ngx_ssl_crl(cf, plcf->upstream.ssl, &plcf->ssl_crl) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

#endif

static void

‌ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v)

{

    if (u->family != AF_UNIX) {

        if (u->no_port || u->port == u->default_port) {

            v->host_header = u->host;

            if (u->default_port == 80) {

                ngx_str_set(&v->port, "80");

            } else {

                ngx_str_set(&v->port, "443");

            }

        } else {

            v->host_header.len = u->host.len + 1 + u->port_text.len;

            v->host_header.data = u->host.data;

            v->port = u->port_text;

        }

        v->key_start.len += v->host_header.len;

    } else {

        ngx_str_set(&v->host_header, "localhost");

        ngx_str_null(&v->port);

        v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1;

    }

    v->uri = u->uri;

}

src/http/modules/ngx_http_random_index_module.c - nginx-1.7.9

Global variables defined

ngx_http_random_index_commands

ngx_http_random_index_module

ngx_http_random_index_module_ctx

Data types defined

ngx_http_random_index_loc_conf_t

Functions defined

ngx_http_random_index_create_loc_conf

ngx_http_random_index_error

ngx_http_random_index_handler

ngx_http_random_index_init

ngx_http_random_index_merge_loc_conf

Macros defined

NGX_HTTP_RANDOM_INDEX_PREALLOCATE

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_flag_t  enable;

‌} ngx_http_random_index_loc_conf_t;

‌#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE  50

static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,

    ngx_dir_t *dir, ngx_str_t *name);

static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);

static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

‌static ngx_command_t  ngx_http_random_index_commands[] = {

    { ngx_string("random_index"),

      NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_random_index_loc_conf_t, enable),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_random_index_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_random_index_init,            /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_random_index_create_loc_conf, /* create location configuration */

    ngx_http_random_index_merge_loc_conf   /* merge location configuration */

};

‌ngx_module_t  ngx_http_random_index_module = {

    NGX_MODULE_V1,

    &ngx_http_random_index_module_ctx,     /* module context */

    ngx_http_random_index_commands,        /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_random_index_handler(ngx_http_request_t *r)

{

    u_char                            *last, *filename;

    size_t                             len, allocated, root;

    ngx_err_t                          err;

    ngx_int_t                          rc;

    ngx_str_t                          path, uri, *name;

    ngx_dir_t                          dir;

    ngx_uint_t                         n, level;

    ngx_array_t                        names;

    ngx_http_random_index_loc_conf_t  *rlcf;

    if (r->uri.data[r->uri.len - 1] != '/') {

        return NGX_DECLINED;

    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {

        return NGX_DECLINED;

    }

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);

    if (!rlcf->enable) {

        return NGX_DECLINED;

    }

#if (NGX_HAVE_D_TYPE)

    len = NGX_DIR_MASK_LEN;

#else

    len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;

#endif

    last = ngx_http_map_uri_to_path(r, &path, &root, len);

    if (last == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    allocated = path.len;

    path.len = last - path.data - 1;

    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http random index: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {

        err = ngx_errno;

        if (err == NGX_ENOENT

            || err == NGX_ENOTDIR

            || err == NGX_ENAMETOOLONG)

        {

            level = NGX_LOG_ERR;

            rc = NGX_HTTP_NOT_FOUND;

        } else if (err == NGX_EACCES) {

            level = NGX_LOG_ERR;

            rc = NGX_HTTP_FORBIDDEN;

        } else {

            level = NGX_LOG_CRIT;

            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

        ngx_log_error(level, r->connection->log, err,

                      ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;

    }

    if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {

        return ngx_http_random_index_error(r, &dir, &path);

    }

    filename = path.data;

    filename[path.len] = '/';

    for ( ;; ) {

        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {

            err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {

                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,

                              ngx_read_dir_n " \"%V\" failed", &path);

                return ngx_http_random_index_error(r, &dir, &path);

            }

            break;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http random index file: \"%s\"", ngx_de_name(&dir));

        if (ngx_de_name(&dir)[0] == '.') {

            continue;

        }

        len = ngx_de_namelen(&dir);

        if (dir.type == 0 || ngx_de_is_link(&dir)) {

            /* 1 byte for '/' and 1 byte for terminating '\0' */

            if (path.len + 1 + len + 1 > allocated) {

                allocated = path.len + 1 + len + 1

                                     + NGX_HTTP_RANDOM_INDEX_PREALLOCATE;

                filename = ngx_pnalloc(r->pool, allocated);

                if (filename == NULL) {

                    return ngx_http_random_index_error(r, &dir, &path);

                }

                last = ngx_cpystrn(filename, path.data, path.len + 1);

                *last++ = '/';

            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {

                err = ngx_errno;

                if (err != NGX_ENOENT) {

                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,

                                  ngx_de_info_n " \"%s\" failed", filename);

                    return ngx_http_random_index_error(r, &dir, &path);

                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {

                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,

                                  ngx_de_link_info_n " \"%s\" failed",

                                  filename);

                    return ngx_http_random_index_error(r, &dir, &path);

                }

            }

        }

        if (!ngx_de_is_file(&dir)) {

            continue;

        }

        name = ngx_array_push(&names);

        if (name == NULL) {

            return ngx_http_random_index_error(r, &dir, &path);

        }

        name->len = len;

        name->data = ngx_pnalloc(r->pool, len);

        if (name->data == NULL) {

            return ngx_http_random_index_error(r, &dir, &path);

        }

        ngx_memcpy(name->data, ngx_de_name(&dir), len);

    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                      ngx_close_dir_n " \"%s\" failed", &path);

    }

    n = names.nelts;

    if (n == 0) {

        return NGX_DECLINED;

    }

    name = names.elts;

    n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);

    uri.len = r->uri.len + name[n].len;

    uri.data = ngx_pnalloc(r->pool, uri.len);

    if (uri.data == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

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

    ngx_memcpy(last, name[n].data, name[n].len);

    return ngx_http_internal_redirect(r, &uri, &r->args);

}

static ngx_int_t

‌ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,

    ngx_str_t *name)

{

    if (ngx_close_dir(dir) == NGX_ERROR) {

        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,

                      ngx_close_dir_n " \"%V\" failed", name);

    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;

}

static void *

‌ngx_http_random_index_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_random_index_loc_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->enable = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_random_index_loc_conf_t *prev = parent;

    ngx_http_random_index_loc_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_random_index_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_random_index_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_range_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_body_filter

ngx_http_next_header_filter

ngx_http_range_body_filter_module

ngx_http_range_body_filter_module_ctx

ngx_http_range_header_filter_module

ngx_http_range_header_filter_module_ctx

Data types defined

ngx_http_range_filter_ctx_t

ngx_http_range_t

Functions defined

ngx_http_range_body_filter

ngx_http_range_body_filter_init

ngx_http_range_header_filter

ngx_http_range_header_filter_init

ngx_http_range_multipart_body

ngx_http_range_multipart_header

ngx_http_range_not_satisfiable

ngx_http_range_parse

ngx_http_range_singlepart_body

ngx_http_range_singlepart_header

ngx_http_range_test_overlapped

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

/*

* the single part format:

*

* "HTTP/1.0 206 Partial Content" CRLF

* ... header ...

* "Content-Type: image/jpeg" CRLF

* "Content-Length: SIZE" CRLF

* "Content-Range: bytes START-END/SIZE" CRLF

* CRLF

* ... data ...

*

*

* the multipart format:

*

* "HTTP/1.0 206 Partial Content" CRLF

* ... header ...

* "Content-Type: multipart/byteranges; boundary=0123456789" CRLF

* CRLF

* CRLF

* "--0123456789" CRLF

* "Content-Type: image/jpeg" CRLF

* "Content-Range: bytes START0-END0/SIZE" CRLF

* CRLF

* ... data ...

* CRLF

* "--0123456789" CRLF

* "Content-Type: image/jpeg" CRLF

* "Content-Range: bytes START1-END1/SIZE" CRLF

* CRLF

* ... data ...

* CRLF

* "--0123456789--" CRLF

*/

typedef struct {

    off_t        start;

    off_t        end;

    ngx_str_t    content_range;

‌} ngx_http_range_t;

typedef struct {

    off_t        offset;

    ngx_str_t    boundary_header;

    ngx_array_t  ranges;

‌} ngx_http_range_filter_ctx_t;

static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);

static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx);

static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx);

static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);

static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);

static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);

static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);

static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);

static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);

‌static ngx_http_module_t  ngx_http_range_header_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_range_header_filter_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 */

};

‌ngx_module_t  ngx_http_range_header_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_range_header_filter_module_ctx, /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_module_t  ngx_http_range_body_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_range_body_filter_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 */

};

‌ngx_module_t  ngx_http_range_body_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_range_body_filter_module_ctx, /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_range_header_filter(ngx_http_request_t *r)

{

    time_t                        if_range_time;

    ngx_str_t                    *if_range, *etag;

    ngx_uint_t                    ranges;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_range_filter_ctx_t  *ctx;

    if (r->http_version < NGX_HTTP_VERSION_10

        || r->headers_out.status != NGX_HTTP_OK

        || r != r->main

        || r->headers_out.content_length_n == -1

        || !r->allow_ranges)

    {

        return ngx_http_next_header_filter(r);

    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->max_ranges == 0) {

        return ngx_http_next_header_filter(r);

    }

    if (r->headers_in.range == NULL

        || r->headers_in.range->value.len < 7

        || ngx_strncasecmp(r->headers_in.range->value.data,

                           (u_char *) "bytes=", 6)

           != 0)

    {

        goto next_filter;

    }

    if (r->headers_in.if_range) {

        if_range = &r->headers_in.if_range->value;

        if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {

            if (r->headers_out.etag == NULL) {

                goto next_filter;

            }

            etag = &r->headers_out.etag->value;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http ir:%V etag:%V", if_range, etag);

            if (if_range->len != etag->len

                || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)

            {

                goto next_filter;

            }

            goto parse;

        }

        if (r->headers_out.last_modified_time == (time_t) -1) {

            goto next_filter;

        }

        if_range_time = ngx_http_parse_time(if_range->data, if_range->len);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http ir:%d lm:%d",

                       if_range_time, r->headers_out.last_modified_time);

        if (if_range_time != r->headers_out.last_modified_time) {

            goto next_filter;

        }

    }

parse:

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    ranges = r->single_range ? 1 : clcf->max_ranges;

    switch (ngx_http_range_parse(r, ctx, ranges)) {

    case NGX_OK:

        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);

        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;

        r->headers_out.status_line.len = 0;

        if (ctx->ranges.nelts == 1) {

            return ngx_http_range_singlepart_header(r, ctx);

        }

        return ngx_http_range_multipart_header(r, ctx);

    case NGX_HTTP_RANGE_NOT_SATISFIABLE:

        return ngx_http_range_not_satisfiable(r);

    case NGX_ERROR:

        return NGX_ERROR;

    default: /* NGX_DECLINED */

        break;

    }

next_filter:

    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);

    if (r->headers_out.accept_ranges == NULL) {

        return NGX_ERROR;

    }

    r->headers_out.accept_ranges->hash = 1;

    ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");

    ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,

    ngx_uint_t ranges)

{

    u_char            *p;

    off_t              start, end, size, content_length;

    ngx_uint_t         suffix;

    ngx_http_range_t  *range;

    p = r->headers_in.range->value.data + 6;

    size = 0;

    content_length = r->headers_out.content_length_n;

    for ( ;; ) {

        start = 0;

        end = 0;

        suffix = 0;

        while (*p == ' ') { p++; }

        if (*p != '-') {

            if (*p < '0' || *p > '9') {

                return NGX_HTTP_RANGE_NOT_SATISFIABLE;

            }

            while (*p >= '0' && *p <= '9') {

                start = start * 10 + *p++ - '0';

            }

            while (*p == ' ') { p++; }

            if (*p++ != '-') {

                return NGX_HTTP_RANGE_NOT_SATISFIABLE;

            }

            while (*p == ' ') { p++; }

            if (*p == ',' || *p == '\0') {

                end = content_length;

                goto found;

            }

        } else {

            suffix = 1;

            p++;

        }

        if (*p < '0' || *p > '9') {

            return NGX_HTTP_RANGE_NOT_SATISFIABLE;

        }

        while (*p >= '0' && *p <= '9') {

            end = end * 10 + *p++ - '0';

        }

        while (*p == ' ') { p++; }

        if (*p != ',' && *p != '\0') {

            return NGX_HTTP_RANGE_NOT_SATISFIABLE;

        }

        if (suffix) {

            start = content_length - end;

            end = content_length - 1;

        }

        if (end >= content_length) {

            end = content_length;

        } else {

            end++;

        }

    found:

        if (start < end) {

            range = ngx_array_push(&ctx->ranges);

            if (range == NULL) {

                return NGX_ERROR;

            }

            range->start = start;

            range->end = end;

            size += end - start;

            if (ranges-- == 0) {

                return NGX_DECLINED;

            }

        }

        if (*p++ != ',') {

            break;

        }

    }

    if (ctx->ranges.nelts == 0) {

        return NGX_HTTP_RANGE_NOT_SATISFIABLE;

    }

    if (size > content_length) {

        return NGX_DECLINED;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_range_singlepart_header(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx)

{

    ngx_table_elt_t   *content_range;

    ngx_http_range_t  *range;

    content_range = ngx_list_push(&r->headers_out.headers);

    if (content_range == NULL) {

        return NGX_ERROR;

    }

    r->headers_out.content_range = content_range;

    content_range->hash = 1;

    ngx_str_set(&content_range->key, "Content-Range");

    content_range->value.data = ngx_pnalloc(r->pool,

                                    sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);

    if (content_range->value.data == NULL) {

        return NGX_ERROR;

    }

    /* "Content-Range: bytes SSSS-EEEE/TTTT" header */

    range = ctx->ranges.elts;

    content_range->value.len = ngx_sprintf(content_range->value.data,

                                           "bytes %O-%O/%O",

                                           range->start, range->end - 1,

                                           r->headers_out.content_length_n)

                               - content_range->value.data;

    r->headers_out.content_length_n = range->end - range->start;

    if (r->headers_out.content_length) {

        r->headers_out.content_length->hash = 0;

        r->headers_out.content_length = NULL;

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_range_multipart_header(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx)

{

    size_t              len;

    ngx_uint_t          i;

    ngx_http_range_t   *range;

    ngx_atomic_uint_t   boundary;

    len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN

          + sizeof(CRLF "Content-Type: ") - 1

          + r->headers_out.content_type.len

          + sizeof(CRLF "Content-Range: bytes ") - 1;

    if (r->headers_out.content_type_len == r->headers_out.content_type.len

        && r->headers_out.charset.len)

    {

        len += sizeof("; charset=") - 1 + r->headers_out.charset.len;

    }

    ctx->boundary_header.data = ngx_pnalloc(r->pool, len);

    if (ctx->boundary_header.data == NULL) {

        return NGX_ERROR;

    }

    boundary = ngx_next_temp_number(0);

    /*

     * The boundary header of the range:

     * CRLF

     * "--0123456789" CRLF

     * "Content-Type: image/jpeg" CRLF

     * "Content-Range: bytes "

     */

    if (r->headers_out.content_type_len == r->headers_out.content_type.len

        && r->headers_out.charset.len)

    {

        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,

                                           CRLF "--%0muA" CRLF

                                           "Content-Type: %V; charset=%V" CRLF

                                           "Content-Range: bytes ",

                                           boundary,

                                           &r->headers_out.content_type,

                                           &r->headers_out.charset)

                                   - ctx->boundary_header.data;

    } else if (r->headers_out.content_type.len) {

        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,

                                           CRLF "--%0muA" CRLF

                                           "Content-Type: %V" CRLF

                                           "Content-Range: bytes ",

                                           boundary,

                                           &r->headers_out.content_type)

                                   - ctx->boundary_header.data;

    } else {

        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,

                                           CRLF "--%0muA" CRLF

                                           "Content-Range: bytes ",

                                           boundary)

                                   - ctx->boundary_header.data;

    }

    r->headers_out.content_type.data =

        ngx_pnalloc(r->pool,

                    sizeof("Content-Type: multipart/byteranges; boundary=") - 1

                    + NGX_ATOMIC_T_LEN);

    if (r->headers_out.content_type.data == NULL) {

        return NGX_ERROR;

    }

    r->headers_out.content_type_lowcase = NULL;

    /* "Content-Type: multipart/byteranges; boundary=0123456789" */

    r->headers_out.content_type.len =

                           ngx_sprintf(r->headers_out.content_type.data,

                                       "multipart/byteranges; boundary=%0muA",

                                       boundary)

                           - r->headers_out.content_type.data;

    r->headers_out.content_type_len = r->headers_out.content_type.len;

    r->headers_out.charset.len = 0;

    /* the size of the last boundary CRLF "--0123456789--" CRLF */

    len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;

    range = ctx->ranges.elts;

    for (i = 0; i < ctx->ranges.nelts; i++) {

        /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */

        range[i].content_range.data =

                               ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);

        if (range[i].content_range.data == NULL) {

            return NGX_ERROR;

        }

        range[i].content_range.len = ngx_sprintf(range[i].content_range.data,

                                               "%O-%O/%O" CRLF CRLF,

                                               range[i].start, range[i].end - 1,

                                               r->headers_out.content_length_n)

                                     - range[i].content_range.data;

        len += ctx->boundary_header.len + range[i].content_range.len

                                    + (size_t) (range[i].end - range[i].start);

    }

    r->headers_out.content_length_n = len;

    if (r->headers_out.content_length) {

        r->headers_out.content_length->hash = 0;

        r->headers_out.content_length = NULL;

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_range_not_satisfiable(ngx_http_request_t *r)

{

    ngx_table_elt_t  *content_range;

    r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;

    content_range = ngx_list_push(&r->headers_out.headers);

    if (content_range == NULL) {

        return NGX_ERROR;

    }

    r->headers_out.content_range = content_range;

    content_range->hash = 1;

    ngx_str_set(&content_range->key, "Content-Range");

    content_range->value.data = ngx_pnalloc(r->pool,

                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);

    if (content_range->value.data == NULL) {

        return NGX_ERROR;

    }

    content_range->value.len = ngx_sprintf(content_range->value.data,

                                           "bytes */%O",

                                           r->headers_out.content_length_n)

                               - content_range->value.data;

    ngx_http_clear_content_length(r);

    return NGX_HTTP_RANGE_NOT_SATISFIABLE;

}

static ngx_int_t

‌ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_http_range_filter_ctx_t  *ctx;

    if (in == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);

    if (ctx == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    if (ctx->ranges.nelts == 1) {

        return ngx_http_range_singlepart_body(r, ctx, in);

    }

    /*

     * multipart ranges are supported only if whole body is in a single buffer

     */

    if (ngx_buf_special(in->buf)) {

        return ngx_http_next_body_filter(r, in);

    }

    if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {

        return NGX_ERROR;

    }

    return ngx_http_range_multipart_body(r, ctx, in);

}

static ngx_int_t

‌ngx_http_range_test_overlapped(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)

{

    off_t              start, last;

    ngx_buf_t         *buf;

    ngx_uint_t         i;

    ngx_http_range_t  *range;

    if (ctx->offset) {

        goto overlapped;

    }

    buf = in->buf;

    if (!buf->last_buf) {

        start = ctx->offset;

        last = ctx->offset + ngx_buf_size(buf);

        range = ctx->ranges.elts;

        for (i = 0; i < ctx->ranges.nelts; i++) {

            if (start > range[i].start || last < range[i].end) {

                 goto overlapped;

            }

        }

    }

    ctx->offset = ngx_buf_size(buf);

    return NGX_OK;

overlapped:

    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                  "range in overlapped buffers");

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_range_singlepart_body(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)

{

    off_t              start, last;

    ngx_buf_t         *buf;

    ngx_chain_t       *out, *cl, **ll;

    ngx_http_range_t  *range;

    out = NULL;

    ll = &out;

    range = ctx->ranges.elts;

    for (cl = in; cl; cl = cl->next) {

        buf = cl->buf;

        start = ctx->offset;

        last = ctx->offset + ngx_buf_size(buf);

        ctx->offset = last;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http range body buf: %O-%O", start, last);

        if (ngx_buf_special(buf)) {

            *ll = cl;

            ll = &cl->next;

            continue;

        }

        if (range->end <= start || range->start >= last) {

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http range body skip");

            if (buf->in_file) {

                buf->file_pos = buf->file_last;

            }

            buf->pos = buf->last;

            buf->sync = 1;

            continue;

        }

        if (range->start > start) {

            if (buf->in_file) {

                buf->file_pos += range->start - start;

            }

            if (ngx_buf_in_memory(buf)) {

                buf->pos += (size_t) (range->start - start);

            }

        }

        if (range->end <= last) {

            if (buf->in_file) {

                buf->file_last -= last - range->end;

            }

            if (ngx_buf_in_memory(buf)) {

                buf->last -= (size_t) (last - range->end);

            }

            buf->last_buf = 1;

            *ll = cl;

            cl->next = NULL;

            break;

        }

        *ll = cl;

        ll = &cl->next;

    }

    if (out == NULL) {

        return NGX_OK;

    }

    return ngx_http_next_body_filter(r, out);

}

static ngx_int_t

‌ngx_http_range_multipart_body(ngx_http_request_t *r,

    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)

{

    ngx_buf_t         *b, *buf;

    ngx_uint_t         i;

    ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;

    ngx_http_range_t  *range;

    ll = &out;

    buf = in->buf;

    range = ctx->ranges.elts;

    for (i = 0; i < ctx->ranges.nelts; i++) {

        /*

         * The boundary header of the range:

         * CRLF

         * "--0123456789" CRLF

         * "Content-Type: image/jpeg" CRLF

         * "Content-Range: bytes "

         */

        b = ngx_calloc_buf(r->pool);

        if (b == NULL) {

            return NGX_ERROR;

        }

        b->memory = 1;

        b->pos = ctx->boundary_header.data;

        b->last = ctx->boundary_header.data + ctx->boundary_header.len;

        hcl = ngx_alloc_chain_link(r->pool);

        if (hcl == NULL) {

            return NGX_ERROR;

        }

        hcl->buf = b;

        /* "SSSS-EEEE/TTTT" CRLF CRLF */

        b = ngx_calloc_buf(r->pool);

        if (b == NULL) {

            return NGX_ERROR;

        }

        b->temporary = 1;

        b->pos = range[i].content_range.data;

        b->last = range[i].content_range.data + range[i].content_range.len;

        rcl = ngx_alloc_chain_link(r->pool);

        if (rcl == NULL) {

            return NGX_ERROR;

        }

        rcl->buf = b;

        /* the range data */

        b = ngx_calloc_buf(r->pool);

        if (b == NULL) {

            return NGX_ERROR;

        }

        b->in_file = buf->in_file;

        b->temporary = buf->temporary;

        b->memory = buf->memory;

        b->mmap = buf->mmap;

        b->file = buf->file;

        if (buf->in_file) {

            b->file_pos = buf->file_pos + range[i].start;

            b->file_last = buf->file_pos + range[i].end;

        }

        if (ngx_buf_in_memory(buf)) {

            b->pos = buf->pos + (size_t) range[i].start;

            b->last = buf->pos + (size_t) range[i].end;

        }

        dcl = ngx_alloc_chain_link(r->pool);

        if (dcl == NULL) {

            return NGX_ERROR;

        }

        dcl->buf = b;

        *ll = hcl;

        hcl->next = rcl;

        rcl->next = dcl;

        ll = &dcl->next;

    }

    /* the last boundary CRLF "--0123456789--" CRLF  */

    b = ngx_calloc_buf(r->pool);

    if (b == NULL) {

        return NGX_ERROR;

    }

    b->temporary = 1;

    b->last_buf = 1;

    b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN

                                  + sizeof("--" CRLF) - 1);

    if (b->pos == NULL) {

        return NGX_ERROR;

    }

    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,

                         sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);

    *b->last++ = '-'; *b->last++ = '-';

    *b->last++ = CR; *b->last++ = LF;

    hcl = ngx_alloc_chain_link(r->pool);

    if (hcl == NULL) {

        return NGX_ERROR;

    }

    hcl->buf = b;

    hcl->next = NULL;

    *ll = hcl;

    return ngx_http_next_body_filter(r, out);

}

static ngx_int_t

‌ngx_http_range_header_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_range_header_filter;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_range_body_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_range_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_realip_module.c - nginx-1.7.9

Global variables defined

ngx_http_realip_commands

ngx_http_realip_module

ngx_http_realip_module_ctx

Data types defined

ngx_http_realip_ctx_t

ngx_http_realip_loc_conf_t

Functions defined

ngx_http_realip

ngx_http_realip_cleanup

ngx_http_realip_create_loc_conf

ngx_http_realip_from

ngx_http_realip_handler

ngx_http_realip_init

ngx_http_realip_merge_loc_conf

ngx_http_realip_set_addr

Macros defined

NGX_HTTP_REALIP_HEADER

NGX_HTTP_REALIP_PROXY

NGX_HTTP_REALIP_XFWD

NGX_HTTP_REALIP_XREALIP

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_REALIP_XREALIP  0

‌#define NGX_HTTP_REALIP_XFWD     1

‌#define NGX_HTTP_REALIP_HEADER   2

‌#define NGX_HTTP_REALIP_PROXY    3

typedef struct {

    ngx_array_t       *from;     /* array of ngx_cidr_t */

    ngx_uint_t         type;

    ngx_uint_t         hash;

    ngx_str_t          header;

    ngx_flag_t         recursive;

‌} ngx_http_realip_loc_conf_t;

typedef struct {

    ngx_connection_t  *connection;

    struct sockaddr   *sockaddr;

    socklen_t          socklen;

    ngx_str_t          addr_text;

‌} ngx_http_realip_ctx_t;

static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,

    ngx_addr_t *addr);

static void ngx_http_realip_cleanup(void *data);

static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_realip_commands[] = {

    { ngx_string("set_real_ip_from"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_realip_from,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("real_ip_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_realip,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("real_ip_recursive"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_realip_loc_conf_t, recursive),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_realip_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_realip_init,                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_realip_create_loc_conf,       /* create location configuration */

    ngx_http_realip_merge_loc_conf         /* merge location configuration */

};

‌ngx_module_t  ngx_http_realip_module = {

    NGX_MODULE_V1,

    &ngx_http_realip_module_ctx,           /* module context */

    ngx_http_realip_commands,              /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_realip_handler(ngx_http_request_t *r)

{

    u_char                      *p;

    size_t                       len;

    ngx_str_t                   *value;

    ngx_uint_t                   i, hash;

    ngx_addr_t                   addr;

    ngx_array_t                 *xfwd;

    ngx_list_part_t             *part;

    ngx_table_elt_t             *header;

    ngx_connection_t            *c;

    ngx_http_realip_ctx_t       *ctx;

    ngx_http_realip_loc_conf_t  *rlcf;

    ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);

    if (ctx) {

        return NGX_DECLINED;

    }

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);

    if (rlcf->from == NULL) {

        return NGX_DECLINED;

    }

    switch (rlcf->type) {

    case NGX_HTTP_REALIP_XREALIP:

        if (r->headers_in.x_real_ip == NULL) {

            return NGX_DECLINED;

        }

        value = &r->headers_in.x_real_ip->value;

        xfwd = NULL;

        break;

    case NGX_HTTP_REALIP_XFWD:

        xfwd = &r->headers_in.x_forwarded_for;

        if (xfwd->elts == NULL) {

            return NGX_DECLINED;

        }

        value = NULL;

        break;

    case NGX_HTTP_REALIP_PROXY:

        value = &r->connection->proxy_protocol_addr;

        if (value->len == 0) {

            return NGX_DECLINED;

        }

        xfwd = NULL;

        break;

    default: /* NGX_HTTP_REALIP_HEADER */

        part = &r->headers_in.headers.part;

        header = part->elts;

        hash = rlcf->hash;

        len = rlcf->header.len;

        p = rlcf->header.data;

        for (i = 0; /* void */ ; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (hash == header[i].hash

                && len == header[i].key.len

                && ngx_strncmp(p, header[i].lowcase_key, len) == 0)

            {

                value = &header[i].value;

                xfwd = NULL;

                goto found;

            }

        }

        return NGX_DECLINED;

    }

found:

    c = r->connection;

    addr.sockaddr = c->sockaddr;

    addr.socklen = c->socklen;

    /* addr.name = c->addr_text; */

    if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,

                                    rlcf->recursive)

        != NGX_DECLINED)

    {

        return ngx_http_realip_set_addr(r, &addr);

    }

    return NGX_DECLINED;

}

static ngx_int_t

‌ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)

{

    size_t                  len;

    u_char                 *p;

    u_char                  text[NGX_SOCKADDR_STRLEN];

    ngx_connection_t       *c;

    ngx_pool_cleanup_t     *cln;

    ngx_http_realip_ctx_t  *ctx;

    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));

    if (cln == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ctx = cln->data;

    ngx_http_set_ctx(r, ctx, ngx_http_realip_module);

    c = r->connection;

    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,

                        NGX_SOCKADDR_STRLEN, 0);

    if (len == 0) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    p = ngx_pnalloc(c->pool, len);

    if (p == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_memcpy(p, text, len);

    cln->handler = ngx_http_realip_cleanup;

    ctx->connection = c;

    ctx->sockaddr = c->sockaddr;

    ctx->socklen = c->socklen;

    ctx->addr_text = c->addr_text;

    c->sockaddr = addr->sockaddr;

    c->socklen = addr->socklen;

    c->addr_text.len = len;

    c->addr_text.data = p;

    return NGX_DECLINED;

}

static void

‌ngx_http_realip_cleanup(void *data)

{

    ngx_http_realip_ctx_t *ctx = data;

    ngx_connection_t  *c;

    c = ctx->connection;

    c->sockaddr = ctx->sockaddr;

    c->socklen = ctx->socklen;

    c->addr_text = ctx->addr_text;

}

static char *

‌ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_realip_loc_conf_t *rlcf = conf;

    ngx_int_t                rc;

    ngx_str_t               *value;

    ngx_cidr_t              *cidr;

    value = cf->args->elts;

    if (rlcf->from == NULL) {

        rlcf->from = ngx_array_create(cf->pool, 2,

                                      sizeof(ngx_cidr_t));

        if (rlcf->from == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    cidr = ngx_array_push(rlcf->from);

    if (cidr == NULL) {

        return NGX_CONF_ERROR;

    }

#if (NGX_HAVE_UNIX_DOMAIN)

    if (ngx_strcmp(value[1].data, "unix:") == 0) {

         cidr->family = AF_UNIX;

         return NGX_CONF_OK;

    }

#endif

    rc = ngx_ptocidr(&value[1], cidr);

    if (rc == NGX_ERROR) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",

                           &value[1]);

        return NGX_CONF_ERROR;

    }

    if (rc == NGX_DONE) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "low address bits of %V are meaningless", &value[1]);

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_realip_loc_conf_t *rlcf = conf;

    ngx_str_t  *value;

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {

        rlcf->type = NGX_HTTP_REALIP_XREALIP;

        return NGX_CONF_OK;

    }

    if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {

        rlcf->type = NGX_HTTP_REALIP_XFWD;

        return NGX_CONF_OK;

    }

    if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {

        rlcf->type = NGX_HTTP_REALIP_PROXY;

        return NGX_CONF_OK;

    }

    rlcf->type = NGX_HTTP_REALIP_HEADER;

    rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);

    rlcf->header = value[1];

    return NGX_CONF_OK;

}

static void *

‌ngx_http_realip_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_realip_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->from = NULL;

     *     conf->hash = 0;

     *     conf->header = { 0, NULL };

     */

    conf->type = NGX_CONF_UNSET_UINT;

    conf->recursive = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_realip_loc_conf_t  *prev = parent;

    ngx_http_realip_loc_conf_t  *conf = child;

    if (conf->from == NULL) {

        conf->from = prev->from;

    }

    ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);

    ngx_conf_merge_value(conf->recursive, prev->recursive, 0);

    if (conf->header.len == 0) {

        conf->hash = prev->hash;

        conf->header = prev->header;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_realip_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_POST_READ_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_realip_handler;

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_realip_handler;

    return NGX_OK;

}

src/http/modules/ngx_http_referer_module.c - nginx-1.7.9

Global variables defined

ngx_http_referer_commands

ngx_http_referer_module

ngx_http_referer_module_ctx

Data types defined

ngx_http_referer_conf_t

Functions defined

ngx_http_add_referer

ngx_http_add_regex_referer

ngx_http_add_regex_server_name

ngx_http_cmp_referer_wildcards

ngx_http_referer_create_conf

ngx_http_referer_merge_conf

ngx_http_referer_variable

ngx_http_valid_referers

Macros defined

NGX_HTTP_REFERER_NO_URI_PART

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

‌#define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)

typedef struct {

    ngx_hash_combined_t      hash;

#if (NGX_PCRE)

    ngx_array_t             *regex;

    ngx_array_t             *server_name_regex;

#endif

    ngx_flag_t               no_referer;

    ngx_flag_t               blocked_referer;

    ngx_flag_t               server_names;

    ngx_hash_keys_arrays_t  *keys;

    ngx_uint_t               referer_hash_max_size;

    ngx_uint_t               referer_hash_bucket_size;

‌} ngx_http_referer_conf_t;

static void * ngx_http_referer_create_conf(ngx_conf_t *cf);

static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,

    ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);

static ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,

    ngx_http_referer_conf_t *rlcf, ngx_str_t *name);

#if (NGX_PCRE)

static ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,

    ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);

#endif

static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,

    const void *two);

‌static ngx_command_t  ngx_http_referer_commands[] = {

    { ngx_string("valid_referers"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_valid_referers,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("referer_hash_max_size"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_referer_conf_t, referer_hash_max_size),

      NULL },

    { ngx_string("referer_hash_bucket_size"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_referer_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_referer_create_conf,          /* create location configuration */

    ngx_http_referer_merge_conf            /* merge location configuration */

};

‌ngx_module_t  ngx_http_referer_module = {

    NGX_MODULE_V1,

    &ngx_http_referer_module_ctx,          /* module context */

    ngx_http_referer_commands,             /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,

     uintptr_t data)

{

    u_char                    *p, *ref, *last;

    size_t                     len;

    ngx_str_t                 *uri;

    ngx_uint_t                 i, key;

    ngx_http_referer_conf_t   *rlcf;

    u_char                     buf[256];

#if (NGX_PCRE)

    ngx_int_t                  rc;

    ngx_str_t                  referer;

#endif

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);

    if (rlcf->hash.hash.buckets == NULL

        && rlcf->hash.wc_head == NULL

        && rlcf->hash.wc_tail == NULL

#if (NGX_PCRE)

        && rlcf->regex == NULL

        && rlcf->server_name_regex == NULL

#endif

       )

    {

        goto valid;

    }

    if (r->headers_in.referer == NULL) {

        if (rlcf->no_referer) {

            goto valid;

        }

        goto invalid;

    }

    len = r->headers_in.referer->value.len;

    ref = r->headers_in.referer->value.data;

    if (len >= sizeof("http://i.ru") - 1) {

        last = ref + len;

        if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {

            ref += 7;

            len -= 7;

            goto valid_scheme;

        } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {

            ref += 8;

            len -= 8;

            goto valid_scheme;

        }

    }

    if (rlcf->blocked_referer) {

        goto valid;

    }

    goto invalid;

valid_scheme:

    i = 0;

    key = 0;

    for (p = ref; p < last; p++) {

        if (*p == '/' || *p == ':') {

            break;

        }

        if (i == 256) {

            goto invalid;

        }

        buf[i] = ngx_tolower(*p);

        key = ngx_hash(key, buf[i++]);

    }

    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);

    if (uri) {

        goto uri;

    }

#if (NGX_PCRE)

    if (rlcf->server_name_regex) {

        referer.len = p - ref;

        referer.data = buf;

        rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,

                                  r->connection->log);

        if (rc == NGX_OK) {

            goto valid;

        }

        if (rc == NGX_ERROR) {

            return rc;

        }

        /* NGX_DECLINED */

    }

    if (rlcf->regex) {

        referer.len = len;

        referer.data = ref;

        rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);

        if (rc == NGX_OK) {

            goto valid;

        }

        if (rc == NGX_ERROR) {

            return rc;

        }

        /* NGX_DECLINED */

    }

#endif

invalid:

    *v = ngx_http_variable_true_value;

    return NGX_OK;

uri:

    for ( /* void */ ; p < last; p++) {

        if (*p == '/') {

            break;

        }

    }

    len = last - p;

    if (uri == NGX_HTTP_REFERER_NO_URI_PART) {

        goto valid;

    }

    if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {

        goto invalid;

    }

valid:

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static void *

‌ngx_http_referer_create_conf(ngx_conf_t *cf)

{

    ngx_http_referer_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->hash = { NULL };

     *     conf->server_names = 0;

     *     conf->keys = NULL;

     */

#if (NGX_PCRE)

    conf->regex = NGX_CONF_UNSET_PTR;

    conf->server_name_regex = NGX_CONF_UNSET_PTR;

#endif

    conf->no_referer = NGX_CONF_UNSET;

    conf->blocked_referer = NGX_CONF_UNSET;

    conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;

    conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;

    return conf;

}

static char *

‌ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_referer_conf_t *prev = parent;

    ngx_http_referer_conf_t *conf = child;

    ngx_uint_t                 n;

    ngx_hash_init_t            hash;

    ngx_http_server_name_t    *sn;

    ngx_http_core_srv_conf_t  *cscf;

    if (conf->keys == NULL) {

        conf->hash = prev->hash;

#if (NGX_PCRE)

        ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);

        ngx_conf_merge_ptr_value(conf->server_name_regex,

                                 prev->server_name_regex, NULL);

#endif

        ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);

        ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);

        ngx_conf_merge_uint_value(conf->referer_hash_max_size,

                                  prev->referer_hash_max_size, 2048);

        ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,

                                  prev->referer_hash_bucket_size, 64);

        return NGX_CONF_OK;

    }

    if (conf->server_names == 1) {

        cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);

        sn = cscf->server_names.elts;

        for (n = 0; n < cscf->server_names.nelts; n++) {

#if (NGX_PCRE)

            if (sn[n].regex) {

                if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)

                    != NGX_OK)

                {

                    return NGX_CONF_ERROR;

                }

                continue;

            }

#endif

            if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)

                != NGX_OK)

            {

                return NGX_CONF_ERROR;

            }

        }

    }

    if ((conf->no_referer == 1 || conf->blocked_referer == 1)

        && conf->keys->keys.nelts == 0

        && conf->keys->dns_wc_head.nelts == 0

        && conf->keys->dns_wc_tail.nelts == 0)

    {

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "the \"none\" or \"blocked\" referers are specified "

                      "in the \"valid_referers\" directive "

                      "without any valid referer");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_uint_value(conf->referer_hash_max_size,

                              prev->referer_hash_max_size, 2048);

    ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,

                              prev->referer_hash_bucket_size, 64);

    conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,

                                               ngx_cacheline_size);

    hash.key = ngx_hash_key_lc;

    hash.max_size = conf->referer_hash_max_size;

    hash.bucket_size = conf->referer_hash_bucket_size;

    hash.name = "referer_hash";

    hash.pool = cf->pool;

    if (conf->keys->keys.nelts) {

        hash.hash = &conf->hash.hash;

        hash.temp_pool = NULL;

        if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    if (conf->keys->dns_wc_head.nelts) {

        ngx_qsort(conf->keys->dns_wc_head.elts,

                  (size_t) conf->keys->dns_wc_head.nelts,

                  sizeof(ngx_hash_key_t),

                  ngx_http_cmp_referer_wildcards);

        hash.hash = NULL;

        hash.temp_pool = cf->temp_pool;

        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,

                                   conf->keys->dns_wc_head.nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

        conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;

    }

    if (conf->keys->dns_wc_tail.nelts) {

        ngx_qsort(conf->keys->dns_wc_tail.elts,

                  (size_t) conf->keys->dns_wc_tail.nelts,

                  sizeof(ngx_hash_key_t),

                  ngx_http_cmp_referer_wildcards);

        hash.hash = NULL;

        hash.temp_pool = cf->temp_pool;

        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,

                                   conf->keys->dns_wc_tail.nelts)

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

        conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;

    }

#if (NGX_PCRE)

    ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);

    ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,

                             NULL);

#endif

    if (conf->no_referer == NGX_CONF_UNSET) {

        conf->no_referer = 0;

    }

    if (conf->blocked_referer == NGX_CONF_UNSET) {

        conf->blocked_referer = 0;

    }

    conf->keys = NULL;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_referer_conf_t  *rlcf = conf;

    u_char                    *p;

    ngx_str_t                 *value, uri, name;

    ngx_uint_t                 i;

    ngx_http_variable_t       *var;

    ngx_str_set(&name, "invalid_referer");

    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    var->get_handler = ngx_http_referer_variable;

    if (rlcf->keys == NULL) {

        rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));

        if (rlcf->keys == NULL) {

            return NGX_CONF_ERROR;

        }

        rlcf->keys->pool = cf->pool;

        rlcf->keys->temp_pool = cf->pool;

        if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (value[i].len == 0) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid referer \"%V\"", &value[i]);

            return NGX_CONF_ERROR;

        }

        if (ngx_strcmp(value[i].data, "none") == 0) {

            rlcf->no_referer = 1;

            continue;

        }

        if (ngx_strcmp(value[i].data, "blocked") == 0) {

            rlcf->blocked_referer = 1;

            continue;

        }

        if (ngx_strcmp(value[i].data, "server_names") == 0) {

            rlcf->server_names = 1;

            continue;

        }

        if (value[i].data[0] == '~') {

            if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

            continue;

        }

        ngx_str_null(&uri);

        p = (u_char *) ngx_strchr(value[i].data, '/');

        if (p) {

            uri.len = (value[i].data + value[i].len) - p;

            uri.data = p;

            value[i].len = p - value[i].data;

        }

        if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,

    ngx_str_t *value, ngx_str_t *uri)

{

    ngx_int_t   rc;

    ngx_str_t  *u;

    if (uri == NULL || uri->len == 0) {

        u = NGX_HTTP_REFERER_NO_URI_PART;

    } else {

        u = ngx_palloc(cf->pool, sizeof(ngx_str_t));

        if (u == NULL) {

            return NGX_ERROR;

        }

        *u = *uri;

    }

    rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);

    if (rc == NGX_OK) {

        return NGX_OK;

    }

    if (rc == NGX_DECLINED) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid hostname or wildcard \"%V\"", value);

    }

    if (rc == NGX_BUSY) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "conflicting parameter \"%V\"", value);

    }

    return NGX_ERROR;

}

static ngx_int_t

‌ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,

    ngx_str_t *name)

{

#if (NGX_PCRE)

    ngx_regex_elt_t      *re;

    ngx_regex_compile_t   rc;

    u_char                errstr[NGX_MAX_CONF_ERRSTR];

    if (name->len == 1) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);

        return NGX_ERROR;

    }

    if (rlcf->regex == NGX_CONF_UNSET_PTR) {

        rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));

        if (rlcf->regex == NULL) {

            return NGX_ERROR;

        }

    }

    re = ngx_array_push(rlcf->regex);

    if (re == NULL) {

        return NGX_ERROR;

    }

    name->len--;

    name->data++;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pattern = *name;

    rc.pool = cf->pool;

    rc.options = NGX_REGEX_CASELESS;

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

    if (ngx_regex_compile(&rc) != NGX_OK) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);

        return NGX_ERROR;

    }

    re->regex = rc.regex;

    re->name = name->data;

    return NGX_OK;

#else

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "the using of the regex \"%V\" requires PCRE library",

                       name);

    return NGX_ERROR;

#endif

}

#if (NGX_PCRE)

static ngx_int_t

‌ngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,

    ngx_http_regex_t *regex)

{

    ngx_regex_elt_t  *re;

    if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {

        rlcf->server_name_regex = ngx_array_create(cf->pool, 2,

                                                   sizeof(ngx_regex_elt_t));

        if (rlcf->server_name_regex == NULL) {

            return NGX_ERROR;

        }

    }

    re = ngx_array_push(rlcf->server_name_regex);

    if (re == NULL) {

        return NGX_ERROR;

    }

    re->regex = regex->regex;

    re->name = regex->name.data;

    return NGX_OK;

}

#endif

static int ngx_libc_cdecl

‌ngx_http_cmp_referer_wildcards(const void *one, const void *two)

{

    ngx_hash_key_t  *first, *second;

    first = (ngx_hash_key_t *) one;

    second = (ngx_hash_key_t *) two;

    return ngx_dns_strcmp(first->key.data, second->key.data);

}

src/http/modules/ngx_http_rewrite_module.c - nginx-1.7.9

Global variables defined

ngx_http_rewrite_commands

ngx_http_rewrite_module

ngx_http_rewrite_module_ctx

Data types defined

ngx_http_rewrite_loc_conf_t

Functions defined

ngx_http_rewrite

ngx_http_rewrite_break

ngx_http_rewrite_create_loc_conf

ngx_http_rewrite_handler

ngx_http_rewrite_if

ngx_http_rewrite_if_condition

ngx_http_rewrite_init

ngx_http_rewrite_merge_loc_conf

ngx_http_rewrite_return

ngx_http_rewrite_set

ngx_http_rewrite_value

ngx_http_rewrite_var

ngx_http_rewrite_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_array_t  *codes;        /* uintptr_t */

    ngx_uint_t    stack_size;

    ngx_flag_t    log;

    ngx_flag_t    uninitialized_variable_warn;

‌} ngx_http_rewrite_loc_conf_t;

static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_rewrite_init(ngx_conf_t *cf);

static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,

    ngx_http_rewrite_loc_conf_t *lcf);

static char *ngx_http_rewrite_variable(ngx_conf_t *cf,

    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);

static char *ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char * ngx_http_rewrite_value(ngx_conf_t *cf,

    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);

‌static ngx_command_t  ngx_http_rewrite_commands[] = {

    { ngx_string("rewrite"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_TAKE23,

      ngx_http_rewrite,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("return"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_TAKE12,

      ngx_http_rewrite_return,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("break"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_NOARGS,

      ngx_http_rewrite_break,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("if"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,

      ngx_http_rewrite_if,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("set"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_TAKE2,

      ngx_http_rewrite_set,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("rewrite_log"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF

                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, log),

      NULL },

    { ngx_string("uninitialized_variable_warn"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF

                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_rewrite_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_rewrite_init,                 /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_rewrite_create_loc_conf,      /* create location configuration */

    ngx_http_rewrite_merge_loc_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_rewrite_module = {

    NGX_MODULE_V1,

    &ngx_http_rewrite_module_ctx,          /* module context */

    ngx_http_rewrite_commands,             /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_rewrite_handler(ngx_http_request_t *r)

{

    ngx_int_t                     index;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t     *e;

    ngx_http_core_srv_conf_t     *cscf;

    ngx_http_core_main_conf_t    *cmcf;

    ngx_http_rewrite_loc_conf_t  *rlcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    index = cmcf->phase_engine.location_rewrite_index;

    if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {

        /* skipping location rewrite phase for server null location */

        return NGX_DECLINED;

    }

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);

    if (rlcf->codes == NULL) {

        return NGX_DECLINED;

    }

    e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));

    if (e == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    e->sp = ngx_pcalloc(r->pool,

                        rlcf->stack_size * sizeof(ngx_http_variable_value_t));

    if (e->sp == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    e->ip = rlcf->codes->elts;

    e->request = r;

    e->quote = 1;

    e->log = rlcf->log;

    e->status = NGX_DECLINED;

    while (*(uintptr_t *) e->ip) {

        code = *(ngx_http_script_code_pt *) e->ip;

        code(e);

    }

    if (e->status < NGX_HTTP_BAD_REQUEST) {

        return e->status;

    }

    if (r->err_status == 0) {

        return e->status;

    }

    return r->err_status;

}

static ngx_int_t

‌ngx_http_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    ngx_http_variable_t          *var;

    ngx_http_core_main_conf_t    *cmcf;

    ngx_http_rewrite_loc_conf_t  *rlcf;

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);

    if (rlcf->uninitialized_variable_warn == 0) {

        *v = ngx_http_variable_null_value;

        return NGX_OK;

    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    var = cmcf->variables.elts;

    /*

     * the ngx_http_rewrite_module sets variables directly in r->variables,

     * and they should be handled by ngx_http_get_indexed_variable(),

     * so the handler is called only if the variable is not initialized

     */

    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,

                  "using uninitialized \"%V\" variable", &var[data].name);

    *v = ngx_http_variable_null_value;

    return NGX_OK;

}

static void *

‌ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_rewrite_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->stack_size = NGX_CONF_UNSET_UINT;

    conf->log = NGX_CONF_UNSET;

    conf->uninitialized_variable_warn = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_rewrite_loc_conf_t *prev = parent;

    ngx_http_rewrite_loc_conf_t *conf = child;

    uintptr_t  *code;

    ngx_conf_merge_value(conf->log, prev->log, 0);

    ngx_conf_merge_value(conf->uninitialized_variable_warn,

                         prev->uninitialized_variable_warn, 1);

    ngx_conf_merge_uint_value(conf->stack_size, prev->stack_size, 10);

    if (conf->codes == NULL) {

        return NGX_CONF_OK;

    }

    if (conf->codes == prev->codes) {

        return NGX_CONF_OK;

    }

    code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_CONF_ERROR;

    }

    *code = (uintptr_t) NULL;

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_rewrite_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_SERVER_REWRITE_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_rewrite_handler;

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);

    if (h == NULL) {

        return NGX_ERROR;

    }

    *h = ngx_http_rewrite_handler;

    return NGX_OK;

}

static char *

‌ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_rewrite_loc_conf_t  *lcf = conf;

    ngx_str_t                         *value;

    ngx_uint_t                         last;

    ngx_regex_compile_t                rc;

    ngx_http_script_code_pt           *code;

    ngx_http_script_compile_t          sc;

    ngx_http_script_regex_code_t      *regex;

    ngx_http_script_regex_end_code_t  *regex_end;

    u_char                             errstr[NGX_MAX_CONF_ERRSTR];

    regex = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                       sizeof(ngx_http_script_regex_code_t));

    if (regex == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));

    value = cf->args->elts;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.pattern = value[1];

    rc.err.len = NGX_MAX_CONF_ERRSTR;

    rc.err.data = errstr;

    /* TODO: NGX_REGEX_CASELESS */

    regex->regex = ngx_http_regex_compile(cf, &rc);

    if (regex->regex == NULL) {

        return NGX_CONF_ERROR;

    }

    regex->code = ngx_http_script_regex_start_code;

    regex->uri = 1;

    regex->name = value[1];

    if (value[2].data[value[2].len - 1] == '?') {

        /* the last "?" drops the original arguments */

        value[2].len--;

    } else {

        regex->add_args = 1;

    }

    last = 0;

    if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0

        || ngx_strncmp(value[2].data, "https://", sizeof("https://") - 1) == 0

        || ngx_strncmp(value[2].data, "$scheme", sizeof("$scheme") - 1) == 0)

    {

        regex->status = NGX_HTTP_MOVED_TEMPORARILY;

        regex->redirect = 1;

        last = 1;

    }

    if (cf->args->nelts == 4) {

        if (ngx_strcmp(value[3].data, "last") == 0) {

            last = 1;

        } else if (ngx_strcmp(value[3].data, "break") == 0) {

            regex->break_cycle = 1;

            last = 1;

        } else if (ngx_strcmp(value[3].data, "redirect") == 0) {

            regex->status = NGX_HTTP_MOVED_TEMPORARILY;

            regex->redirect = 1;

            last = 1;

        } else if (ngx_strcmp(value[3].data, "permanent") == 0) {

            regex->status = NGX_HTTP_MOVED_PERMANENTLY;

            regex->redirect = 1;

            last = 1;

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid parameter \"%V\"", &value[3]);

            return NGX_CONF_ERROR;

        }

    }

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = &value[2];

    sc.lengths = &regex->lengths;

    sc.values = &lcf->codes;

    sc.variables = ngx_http_script_variables_count(&value[2]);

    sc.main = regex;

    sc.complete_lengths = 1;

    sc.compile_args = !regex->redirect;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    regex = sc.main;

    regex->size = sc.size;

    regex->args = sc.args;

    if (sc.variables == 0 && !sc.dup_capture) {

        regex->lengths = NULL;

    }

    regex_end = ngx_http_script_add_code(lcf->codes,

                                      sizeof(ngx_http_script_regex_end_code_t),

                                      &regex);

    if (regex_end == NULL) {

        return NGX_CONF_ERROR;

    }

    regex_end->code = ngx_http_script_regex_end_code;

    regex_end->uri = regex->uri;

    regex_end->args = regex->args;

    regex_end->add_args = regex->add_args;

    regex_end->redirect = regex->redirect;

    if (last) {

        code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), &regex);

        if (code == NULL) {

            return NGX_CONF_ERROR;

        }

        *code = NULL;

    }

    regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts

                                              - (u_char *) regex;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_rewrite_loc_conf_t  *lcf = conf;

    u_char                            *p;

    ngx_str_t                         *value, *v;

    ngx_http_script_return_code_t     *ret;

    ngx_http_compile_complex_value_t   ccv;

    ret = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                     sizeof(ngx_http_script_return_code_t));

    if (ret == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    ngx_memzero(ret, sizeof(ngx_http_script_return_code_t));

    ret->code = ngx_http_script_return_code;

    p = value[1].data;

    ret->status = ngx_atoi(p, value[1].len);

    if (ret->status == (uintptr_t) NGX_ERROR) {

        if (cf->args->nelts == 2

            && (ngx_strncmp(p, "http://", sizeof("http://") - 1) == 0

                || ngx_strncmp(p, "https://", sizeof("https://") - 1) == 0

                || ngx_strncmp(p, "$scheme", sizeof("$scheme") - 1) == 0))

        {

            ret->status = NGX_HTTP_MOVED_TEMPORARILY;

            v = &value[1];

        } else {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid return code \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

    } else {

        if (ret->status > 999) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid return code \"%V\"", &value[1]);

            return NGX_CONF_ERROR;

        }

        if (cf->args->nelts == 2) {

            return NGX_CONF_OK;

        }

        v = &value[2];

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = v;

    ccv.complex_value = &ret->text;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_rewrite_loc_conf_t *lcf = conf;

    ngx_http_script_code_pt  *code;

    code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_CONF_ERROR;

    }

    *code = ngx_http_script_break_code;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_rewrite_loc_conf_t  *lcf = conf;

    void                         *mconf;

    char                         *rv;

    u_char                       *elts;

    ngx_uint_t                    i;

    ngx_conf_t                    save;

    ngx_http_module_t            *module;

    ngx_http_conf_ctx_t          *ctx, *pctx;

    ngx_http_core_loc_conf_t     *clcf, *pclcf;

    ngx_http_script_if_code_t    *if_code;

    ngx_http_rewrite_loc_conf_t  *nlcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    pctx = cf->ctx;

    ctx->main_conf = pctx->main_conf;

    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

    if (ctx->loc_conf == NULL) {

        return NGX_CONF_ERROR;

    }

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {

            continue;

        }

        module = ngx_modules[i]->ctx;

        if (module->create_loc_conf) {

            mconf = module->create_loc_conf(cf);

            if (mconf == NULL) {

                 return NGX_CONF_ERROR;

            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;

        }

    }

    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

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

    clcf->loc_conf = ctx->loc_conf;

    clcf->name = pclcf->name;

    clcf->noname = 1;

    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {

        return NGX_CONF_ERROR;

    }

    if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));

    if (if_code == NULL) {

        return NGX_CONF_ERROR;

    }

    if_code->code = ngx_http_script_if_code;

    elts = lcf->codes->elts;

    /* the inner directives must be compiled to the same code array */

    nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];

    nlcf->codes = lcf->codes;

    save = *cf;

    cf->ctx = ctx;

    if (pclcf->name.len == 0) {

        if_code->loc_conf = NULL;

        cf->cmd_type = NGX_HTTP_SIF_CONF;

    } else {

        if_code->loc_conf = ctx->loc_conf;

        cf->cmd_type = NGX_HTTP_LIF_CONF;

    }

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    if (elts != lcf->codes->elts) {

        if_code = (ngx_http_script_if_code_t *)

                   ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));

    }

    if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts

                                                - (u_char *) if_code;

    /* the code array belong to parent block */

    nlcf->codes = NULL;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)

{

    u_char                        *p;

    size_t                         len;

    ngx_str_t                     *value;

    ngx_uint_t                     cur, last;

    ngx_regex_compile_t            rc;

    ngx_http_script_code_pt       *code;

    ngx_http_script_file_code_t   *fop;

    ngx_http_script_regex_code_t  *regex;

    u_char                         errstr[NGX_MAX_CONF_ERRSTR];

    value = cf->args->elts;

    last = cf->args->nelts - 1;

    if (value[1].len < 1 || value[1].data[0] != '(') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid condition \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    if (value[1].len == 1) {

        cur = 2;

    } else {

        cur = 1;

        value[1].len--;

        value[1].data++;

    }

    if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid condition \"%V\"", &value[last]);

        return NGX_CONF_ERROR;

    }

    if (value[last].len == 1) {

        last--;

    } else {

        value[last].len--;

        value[last].data[value[last].len] = '\0';

    }

    len = value[cur].len;

    p = value[cur].data;

    if (len > 1 && p[0] == '$') {

        if (cur != last && cur + 2 != last) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid condition \"%V\"", &value[cur]);

            return NGX_CONF_ERROR;

        }

        if (ngx_http_rewrite_variable(cf, lcf, &value[cur]) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

        if (cur == last) {

            return NGX_CONF_OK;

        }

        cur++;

        len = value[cur].len;

        p = value[cur].data;

        if (len == 1 && p[0] == '=') {

            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {

                return NGX_CONF_ERROR;

            }

            code = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                              sizeof(uintptr_t));

            if (code == NULL) {

                return NGX_CONF_ERROR;

            }

            *code = ngx_http_script_equal_code;

            return NGX_CONF_OK;

        }

        if (len == 2 && p[0] == '!' && p[1] == '=') {

            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {

                return NGX_CONF_ERROR;

            }

            code = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                              sizeof(uintptr_t));

            if (code == NULL) {

                return NGX_CONF_ERROR;

            }

            *code = ngx_http_script_not_equal_code;

            return NGX_CONF_OK;

        }

        if ((len == 1 && p[0] == '~')

            || (len == 2 && p[0] == '~' && p[1] == '*')

            || (len == 2 && p[0] == '!' && p[1] == '~')

            || (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*'))

        {

            regex = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                         sizeof(ngx_http_script_regex_code_t));

            if (regex == NULL) {

                return NGX_CONF_ERROR;

            }

            ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));

            ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

            rc.pattern = value[last];

            rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;

            rc.err.len = NGX_MAX_CONF_ERRSTR;

            rc.err.data = errstr;

            regex->regex = ngx_http_regex_compile(cf, &rc);

            if (regex->regex == NULL) {

                return NGX_CONF_ERROR;

            }

            regex->code = ngx_http_script_regex_start_code;

            regex->next = sizeof(ngx_http_script_regex_code_t);

            regex->test = 1;

            if (p[0] == '!') {

                regex->negative_test = 1;

            }

            regex->name = value[last];

            return NGX_CONF_OK;

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "unexpected \"%V\" in condition", &value[cur]);

        return NGX_CONF_ERROR;

    } else if ((len == 2 && p[0] == '-')

               || (len == 3 && p[0] == '!' && p[1] == '-'))

    {

        if (cur + 1 != last) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "invalid condition \"%V\"", &value[cur]);

            return NGX_CONF_ERROR;

        }

        value[last].data[value[last].len] = '\0';

        value[last].len++;

        if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {

            return NGX_CONF_ERROR;

        }

        fop = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                          sizeof(ngx_http_script_file_code_t));

        if (fop == NULL) {

            return NGX_CONF_ERROR;

        }

        fop->code = ngx_http_script_file_code;

        if (p[1] == 'f') {

            fop->op = ngx_http_script_file_plain;

            return NGX_CONF_OK;

        }

        if (p[1] == 'd') {

            fop->op = ngx_http_script_file_dir;

            return NGX_CONF_OK;

        }

        if (p[1] == 'e') {

            fop->op = ngx_http_script_file_exists;

            return NGX_CONF_OK;

        }

        if (p[1] == 'x') {

            fop->op = ngx_http_script_file_exec;

            return NGX_CONF_OK;

        }

        if (p[0] == '!') {

            if (p[2] == 'f') {

                fop->op = ngx_http_script_file_not_plain;

                return NGX_CONF_OK;

            }

            if (p[2] == 'd') {

                fop->op = ngx_http_script_file_not_dir;

                return NGX_CONF_OK;

            }

            if (p[2] == 'e') {

                fop->op = ngx_http_script_file_not_exists;

                return NGX_CONF_OK;

            }

            if (p[2] == 'x') {

                fop->op = ngx_http_script_file_not_exec;

                return NGX_CONF_OK;

            }

        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid condition \"%V\"", &value[cur]);

        return NGX_CONF_ERROR;

    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid condition \"%V\"", &value[cur]);

    return NGX_CONF_ERROR;

}

static char *

‌ngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,

    ngx_str_t *value)

{

    ngx_int_t                    index;

    ngx_http_script_var_code_t  *var_code;

    value->len--;

    value->data++;

    index = ngx_http_get_variable_index(cf, value);

    if (index == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    var_code = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                          sizeof(ngx_http_script_var_code_t));

    if (var_code == NULL) {

        return NGX_CONF_ERROR;

    }

    var_code->code = ngx_http_script_var_code;

    var_code->index = index;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_rewrite_loc_conf_t  *lcf = conf;

    ngx_int_t                            index;

    ngx_str_t                           *value;

    ngx_http_variable_t                 *v;

    ngx_http_script_var_code_t          *vcode;

    ngx_http_script_var_handler_code_t  *vhcode;

    value = cf->args->elts;

    if (value[1].data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &value[1]);

        return NGX_CONF_ERROR;

    }

    value[1].len--;

    value[1].data++;

    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);

    if (v == NULL) {

        return NGX_CONF_ERROR;

    }

    index = ngx_http_get_variable_index(cf, &value[1]);

    if (index == NGX_ERROR) {

        return NGX_CONF_ERROR;

    }

    if (v->get_handler == NULL

        && ngx_strncasecmp(value[1].data, (u_char *) "http_", 5) != 0

        && ngx_strncasecmp(value[1].data, (u_char *) "sent_http_", 10) != 0

        && ngx_strncasecmp(value[1].data, (u_char *) "upstream_http_", 14) != 0

        && ngx_strncasecmp(value[1].data, (u_char *) "cookie_", 7) != 0

        && ngx_strncasecmp(value[1].data, (u_char *) "upstream_cookie_", 16)

           != 0

        && ngx_strncasecmp(value[1].data, (u_char *) "arg_", 4) != 0)

    {

        v->get_handler = ngx_http_rewrite_var;

        v->data = index;

    }

    if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {

        return NGX_CONF_ERROR;

    }

    if (v->set_handler) {

        vhcode = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                   sizeof(ngx_http_script_var_handler_code_t));

        if (vhcode == NULL) {

            return NGX_CONF_ERROR;

        }

        vhcode->code = ngx_http_script_var_set_handler_code;

        vhcode->handler = v->set_handler;

        vhcode->data = v->data;

        return NGX_CONF_OK;

    }

    vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                       sizeof(ngx_http_script_var_code_t));

    if (vcode == NULL) {

        return NGX_CONF_ERROR;

    }

    vcode->code = ngx_http_script_set_var_code;

    vcode->index = (uintptr_t) index;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,

    ngx_str_t *value)

{

    ngx_int_t                              n;

    ngx_http_script_compile_t              sc;

    ngx_http_script_value_code_t          *val;

    ngx_http_script_complex_value_code_t  *complex;

    n = ngx_http_script_variables_count(value);

    if (n == 0) {

        val = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                         sizeof(ngx_http_script_value_code_t));

        if (val == NULL) {

            return NGX_CONF_ERROR;

        }

        n = ngx_atoi(value->data, value->len);

        if (n == NGX_ERROR) {

            n = 0;

        }

        val->code = ngx_http_script_value_code;

        val->value = (uintptr_t) n;

        val->text_len = (uintptr_t) value->len;

        val->text_data = (uintptr_t) value->data;

        return NGX_CONF_OK;

    }

    complex = ngx_http_script_start_code(cf->pool, &lcf->codes,

                                 sizeof(ngx_http_script_complex_value_code_t));

    if (complex == NULL) {

        return NGX_CONF_ERROR;

    }

    complex->code = ngx_http_script_complex_value_code;

    complex->lengths = NULL;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = value;

    sc.lengths = &complex->lengths;

    sc.values = &lcf->codes;

    sc.variables = n;

    sc.complete_lengths = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_scgi_module.c - nginx-1.7.9

Global variables defined

ngx_http_scgi_cache_headers

ngx_http_scgi_commands

ngx_http_scgi_hide_headers

ngx_http_scgi_module

ngx_http_scgi_module

ngx_http_scgi_module_ctx

ngx_http_scgi_next_upstream_masks

ngx_http_scgi_temp_path

Data types defined

ngx_http_scgi_loc_conf_t

ngx_http_scgi_main_conf_t

ngx_http_scgi_params_t

Functions defined

ngx_http_scgi_abort_request

ngx_http_scgi_cache

ngx_http_scgi_cache_key

ngx_http_scgi_create_key

ngx_http_scgi_create_loc_conf

ngx_http_scgi_create_main_conf

ngx_http_scgi_create_request

ngx_http_scgi_eval

ngx_http_scgi_finalize_request

ngx_http_scgi_handler

ngx_http_scgi_init_params

ngx_http_scgi_merge_loc_conf

ngx_http_scgi_pass

ngx_http_scgi_process_header

ngx_http_scgi_process_status_line

ngx_http_scgi_reinit_request

ngx_http_scgi_store

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

* Copyright (C) Manlio Perillo (manlio.perillo@gmail.com)

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_array_t                caches;  /* ngx_http_file_cache_t * */

‌} ngx_http_scgi_main_conf_t;

typedef struct {

    ngx_array_t               *flushes;

    ngx_array_t               *lengths;

    ngx_array_t               *values;

    ngx_uint_t                 number;

    ngx_hash_t                 hash;

‌} ngx_http_scgi_params_t;

typedef struct {

    ngx_http_upstream_conf_t   upstream;

    ngx_http_scgi_params_t     params;

#if (NGX_HTTP_CACHE)

    ngx_http_scgi_params_t     params_cache;

#endif

    ngx_array_t               *params_source;

    ngx_array_t               *scgi_lengths;

    ngx_array_t               *scgi_values;

#if (NGX_HTTP_CACHE)

    ngx_http_complex_value_t   cache_key;

#endif

‌} ngx_http_scgi_loc_conf_t;

static ngx_int_t ngx_http_scgi_eval(ngx_http_request_t *r,

    ngx_http_scgi_loc_conf_t *scf);

static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);

static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);

static void ngx_http_scgi_abort_request(ngx_http_request_t *r);

static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);

static void *ngx_http_scgi_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_scgi_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_scgi_init_params(ngx_conf_t *cf,

    ngx_http_scgi_loc_conf_t *conf, ngx_http_scgi_params_t *params,

    ngx_keyval_t *default_params);

static char *ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#if (NGX_HTTP_CACHE)

static ngx_int_t ngx_http_scgi_create_key(ngx_http_request_t *r);

static char *ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

‌static ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = {

    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },

    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },

    { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },

    { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },

    { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },

    { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },

    { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },

    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },

    { ngx_null_string, 0 }

};

‌ngx_module_t  ngx_http_scgi_module;

‌static ngx_command_t ngx_http_scgi_commands[] = {

    { ngx_string("scgi_pass"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_scgi_pass,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("scgi_store"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_scgi_store,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("scgi_store_access"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_conf_set_access_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.store_access),

      NULL },

    { ngx_string("scgi_buffering"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.buffering),

      NULL },

    { ngx_string("scgi_ignore_client_abort"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_client_abort),

      NULL },

    { ngx_string("scgi_bind"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_bind_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.local),

      NULL },

    { ngx_string("scgi_connect_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.connect_timeout),

      NULL },

    { ngx_string("scgi_send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.send_timeout),

      NULL },

    { ngx_string("scgi_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.buffer_size),

      NULL },

    { ngx_string("scgi_pass_request_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_headers),

      NULL },

    { ngx_string("scgi_pass_request_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_body),

      NULL },

    { ngx_string("scgi_intercept_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.intercept_errors),

      NULL },

    { ngx_string("scgi_read_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.read_timeout),

      NULL },

    { ngx_string("scgi_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.bufs),

      NULL },

    { ngx_string("scgi_busy_buffers_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf),

      NULL },

    { ngx_string("scgi_force_ranges"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.force_ranges),

      NULL },

    { ngx_string("scgi_limit_rate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.limit_rate),

      NULL },

#if (NGX_HTTP_CACHE)

    { ngx_string("scgi_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_scgi_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("scgi_cache_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_scgi_cache_key,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("scgi_cache_path"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,

      ngx_http_file_cache_set_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_scgi_main_conf_t, caches),

      &ngx_http_scgi_module },

    { ngx_string("scgi_cache_bypass"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_bypass),

      NULL },

    { ngx_string("scgi_no_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.no_cache),

      NULL },

    { ngx_string("scgi_cache_valid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_file_cache_valid_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_valid),

      NULL },

    { ngx_string("scgi_cache_min_uses"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_min_uses),

      NULL },

    { ngx_string("scgi_cache_use_stale"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_use_stale),

      &ngx_http_scgi_next_upstream_masks },

    { ngx_string("scgi_cache_methods"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_methods),

      &ngx_http_upstream_cache_method_mask },

    { ngx_string("scgi_cache_lock"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock),

      NULL },

    { ngx_string("scgi_cache_lock_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout),

      NULL },

    { ngx_string("scgi_cache_lock_age"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_age),

      NULL },

    { ngx_string("scgi_cache_revalidate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_revalidate),

      NULL },

#endif

    { ngx_string("scgi_temp_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_conf_set_path_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_path),

      NULL },

    { ngx_string("scgi_max_temp_file_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.max_temp_file_size_conf),

      NULL },

    { ngx_string("scgi_temp_file_write_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_file_write_size_conf),

      NULL },

    { ngx_string("scgi_next_upstream"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream),

      &ngx_http_scgi_next_upstream_masks },

    { ngx_string("scgi_next_upstream_tries"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream_tries),

      NULL },

    { ngx_string("scgi_next_upstream_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream_timeout),

      NULL },

    { ngx_string("scgi_param"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,

      ngx_http_upstream_param_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, params_source),

      NULL },

    { ngx_string("scgi_pass_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_headers),

      NULL },

    { ngx_string("scgi_hide_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.hide_headers),

      NULL },

    { ngx_string("scgi_ignore_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_headers),

      &ngx_http_upstream_ignore_headers_masks },

      ngx_null_command

};

‌static ngx_http_module_t ngx_http_scgi_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_scgi_create_main_conf,        /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_scgi_create_loc_conf,         /* create location configuration */

    ngx_http_scgi_merge_loc_conf           /* merge location configuration */

};

‌ngx_module_t ngx_http_scgi_module = {

    NGX_MODULE_V1,

    &ngx_http_scgi_module_ctx,             /* module context */

    ngx_http_scgi_commands,                /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t ngx_http_scgi_hide_headers[] = {

    ngx_string("Status"),

    ngx_string("X-Accel-Expires"),

    ngx_string("X-Accel-Redirect"),

    ngx_string("X-Accel-Limit-Rate"),

    ngx_string("X-Accel-Buffering"),

    ngx_string("X-Accel-Charset"),

    ngx_null_string

};

#if (NGX_HTTP_CACHE)

‌static ngx_keyval_t  ngx_http_scgi_cache_headers[] = {

    { ngx_string("HTTP_IF_MODIFIED_SINCE"),

      ngx_string("$upstream_cache_last_modified") },

    { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },

    { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },

    { ngx_string("HTTP_IF_MATCH"), ngx_string("") },

    { ngx_string("HTTP_RANGE"), ngx_string("") },

    { ngx_string("HTTP_IF_RANGE"), ngx_string("") },

    { ngx_null_string, ngx_null_string }

};

#endif

‌static ngx_path_init_t ngx_http_scgi_temp_path = {

    ngx_string(NGX_HTTP_SCGI_TEMP_PATH), { 1, 2, 0 }

};

static ngx_int_t

‌ngx_http_scgi_handler(ngx_http_request_t *r)

{

    ngx_int_t                   rc;

    ngx_http_status_t          *status;

    ngx_http_upstream_t        *u;

    ngx_http_scgi_loc_conf_t   *scf;

#if (NGX_HTTP_CACHE)

    ngx_http_scgi_main_conf_t  *smcf;

#endif

    if (ngx_http_upstream_create(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));

    if (status == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_http_set_ctx(r, status, ngx_http_scgi_module);

    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);

    if (scf->scgi_lengths) {

        if (ngx_http_scgi_eval(r, scf) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    u = r->upstream;

    ngx_str_set(&u->schema, "scgi://");

    u->output.tag = (ngx_buf_tag_t) &ngx_http_scgi_module;

    u->conf = &scf->upstream;

#if (NGX_HTTP_CACHE)

    smcf = ngx_http_get_module_main_conf(r, ngx_http_scgi_module);

    u->caches = &smcf->caches;

    u->create_key = ngx_http_scgi_create_key;

#endif

    u->create_request = ngx_http_scgi_create_request;

    u->reinit_request = ngx_http_scgi_reinit_request;

    u->process_header = ngx_http_scgi_process_status_line;

    u->abort_request = ngx_http_scgi_abort_request;

    u->finalize_request = ngx_http_scgi_finalize_request;

    r->state = 0;

    u->buffering = scf->upstream.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));

    if (u->pipe == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;

    u->pipe->input_ctx = r;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        return rc;

    }

    return NGX_DONE;

}

static ngx_int_t

‌ngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf)

{

    ngx_url_t             url;

    ngx_http_upstream_t  *u;

    ngx_memzero(&url, sizeof(ngx_url_t));

    if (ngx_http_script_run(r, &url.url, scf->scgi_lengths->elts, 0,

                            scf->scgi_values->elts)

        == NULL)

    {

        return NGX_ERROR;

    }

    url.no_resolve = 1;

    if (ngx_parse_url(r->pool, &url) != NGX_OK) {

        if (url.err) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "%s in upstream \"%V\"", url.err, &url.url);

        }

        return NGX_ERROR;

    }

    u = r->upstream;

    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));

    if (u->resolved == NULL) {

        return NGX_ERROR;

    }

    if (url.addrs && url.addrs[0].sockaddr) {

        u->resolved->sockaddr = url.addrs[0].sockaddr;

        u->resolved->socklen = url.addrs[0].socklen;

        u->resolved->naddrs = 1;

        u->resolved->host = url.addrs[0].name;

    } else {

        u->resolved->host = url.host;

        u->resolved->port = url.port;

        u->resolved->no_port = url.no_port;

    }

    return NGX_OK;

}

#if (NGX_HTTP_CACHE)

static ngx_int_t

‌ngx_http_scgi_create_key(ngx_http_request_t *r)

{

    ngx_str_t                 *key;

    ngx_http_scgi_loc_conf_t  *scf;

    key = ngx_array_push(&r->cache->keys);

    if (key == NULL) {

        return NGX_ERROR;

    }

    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);

    if (ngx_http_complex_value(r, &scf->cache_key, key) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_scgi_create_request(ngx_http_request_t *r)

{

    off_t                         content_length_n;

    u_char                        ch, *key, *val, *lowcase_key;

    size_t                        len, key_len, val_len, allocated;

    ngx_buf_t                    *b;

    ngx_str_t                     content_length;

    ngx_uint_t                    i, n, hash, skip_empty, header_params;

    ngx_chain_t                  *cl, *body;

    ngx_list_part_t              *part;

    ngx_table_elt_t              *header, **ignored;

    ngx_http_scgi_params_t       *params;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t      e, le;

    ngx_http_scgi_loc_conf_t     *scf;

    ngx_http_script_len_code_pt   lcode;

    u_char                        buffer[NGX_OFF_T_LEN];

    content_length_n = 0;

    body = r->upstream->request_bufs;

    while (body) {

        content_length_n += ngx_buf_size(body->buf);

        body = body->next;

    }

    content_length.data = buffer;

    content_length.len = ngx_sprintf(buffer, "%O", content_length_n) - buffer;

    len = sizeof("CONTENT_LENGTH") + content_length.len + 1;

    header_params = 0;

    ignored = NULL;

    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);

#if (NGX_HTTP_CACHE)

    params = r->upstream->cacheable ? &scf->params_cache : &scf->params;

#else

    params = &scf->params;

#endif

    if (params->lengths) {

        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);

        le.flushed = 1;

        le.ip = params->lengths->elts;

        le.request = r;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            key_len = lcode(&le);

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                continue;

            }

            len += key_len + val_len + 1;

        }

    }

    if (scf->upstream.pass_request_headers) {

        allocated = 0;

        lowcase_key = NULL;

        if (params->number) {

            n = 0;

            part = &r->headers_in.headers.part;

            while (part) {

                n += part->nelts;

                part = part->next;

            }

            ignored = ngx_palloc(r->pool, n * sizeof(void *));

            if (ignored == NULL) {

                return NGX_ERROR;

            }

        }

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (params->number) {

                if (allocated < header[i].key.len) {

                    allocated = header[i].key.len + 16;

                    lowcase_key = ngx_pnalloc(r->pool, allocated);

                    if (lowcase_key == NULL) {

                        return NGX_ERROR;

                    }

                }

                hash = 0;

                for (n = 0; n < header[i].key.len; n++) {

                    ch = header[i].key.data[n];

                    if (ch >= 'A' && ch <= 'Z') {

                        ch |= 0x20;

                    } else if (ch == '-') {

                        ch = '_';

                    }

                    hash = ngx_hash(hash, ch);

                    lowcase_key[n] = ch;

                }

                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {

                    ignored[header_params++] = &header[i];

                    continue;

                }

            }

            len += sizeof("HTTP_") - 1 + header[i].key.len + 1

                + header[i].value.len + 1;

        }

    }

    /* netstring: "length:" + packet + "," */

    b = ngx_create_temp_buf(r->pool, NGX_SIZE_T_LEN + 1 + len + 1);

    if (b == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    b->last = ngx_sprintf(b->last, "%ui:CONTENT_LENGTH%Z%V%Z",

                          len, &content_length);

    if (params->lengths) {

        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

        e.ip = params->values->elts;

        e.pos = b->last;

        e.request = r;

        e.flushed = 1;

        le.ip = params->lengths->elts;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            lcode(&le); /* key length */

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                e.skip = 1;

                while (*(uintptr_t *) e.ip) {

                    code = *(ngx_http_script_code_pt *) e.ip;

                    code((ngx_http_script_engine_t *) &e);

                }

                e.ip += sizeof(uintptr_t);

                e.skip = 0;

                continue;

            }

#if (NGX_DEBUG)

            key = e.pos;

#endif

            code = *(ngx_http_script_code_pt *) e.ip;

            code((ngx_http_script_engine_t *) & e);

#if (NGX_DEBUG)

            val = e.pos;

#endif

            while (*(uintptr_t *) e.ip) {

                code = *(ngx_http_script_code_pt *) e.ip;

                code((ngx_http_script_engine_t *) &e);

            }

            *e.pos++ = '\0';

            e.ip += sizeof(uintptr_t);

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "scgi param: \"%s: %s\"", key, val);

        }

        b->last = e.pos;

    }

    if (scf->upstream.pass_request_headers) {

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            for (n = 0; n < header_params; n++) {

                if (&header[i] == ignored[n]) {

                    goto next;

                }

            }

            key = b->last;

            b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1);

            for (n = 0; n < header[i].key.len; n++) {

                ch = header[i].key.data[n];

                if (ch >= 'a' && ch <= 'z') {

                    ch &= ~0x20;

                } else if (ch == '-') {

                    ch = '_';

                }

                *b->last++ = ch;

            }

            *b->last++ = (u_char) 0;

            val = b->last;

            b->last = ngx_copy(val, header[i].value.data, header[i].value.len);

            *b->last++ = (u_char) 0;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "scgi param: \"%s: %s\"", key, val);

        next:

            continue;

         }

    }

    *b->last++ = (u_char) ',';

    if (scf->upstream.pass_request_body) {

        body = r->upstream->request_bufs;

        r->upstream->request_bufs = cl;

        while (body) {

            b = ngx_alloc_buf(r->pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

            cl->next = ngx_alloc_chain_link(r->pool);

            if (cl->next == NULL) {

                return NGX_ERROR;

            }

            cl = cl->next;

            cl->buf = b;

            body = body->next;

        }

    } else {

        r->upstream->request_bufs = cl;

    }

    cl->next = NULL;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_scgi_reinit_request(ngx_http_request_t *r)

{

    ngx_http_status_t  *status;

    status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);

    if (status == NULL) {

        return NGX_OK;

    }

    status->code = 0;

    status->count = 0;

    status->start = NULL;

    status->end = NULL;

    r->upstream->process_header = ngx_http_scgi_process_status_line;

    r->state = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_scgi_process_status_line(ngx_http_request_t *r)

{

    size_t                len;

    ngx_int_t             rc;

    ngx_http_status_t    *status;

    ngx_http_upstream_t  *u;

    status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);

    if (status == NULL) {

        return NGX_ERROR;

    }

    u = r->upstream;

    rc = ngx_http_parse_status_line(r, &u->buffer, status);

    if (rc == NGX_AGAIN) {

        return rc;

    }

    if (rc == NGX_ERROR) {

        u->process_header = ngx_http_scgi_process_header;

        return ngx_http_scgi_process_header(r);

    }

    if (u->state && u->state->status == 0) {

        u->state->status = status->code;

    }

    u->headers_in.status_n = status->code;

    len = status->end - status->start;

    u->headers_in.status_line.len = len;

    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);

    if (u->headers_in.status_line.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(u->headers_in.status_line.data, status->start, len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http scgi status %ui \"%V\"",

                   u->headers_in.status_n, &u->headers_in.status_line);

    u->process_header = ngx_http_scgi_process_header;

    return ngx_http_scgi_process_header(r);

}

static ngx_int_t

‌ngx_http_scgi_process_header(ngx_http_request_t *r)

{

    ngx_str_t                      *status_line;

    ngx_int_t                       rc, status;

    ngx_table_elt_t                *h;

    ngx_http_upstream_t            *u;

    ngx_http_upstream_header_t     *hh;

    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    for ( ;; ) {

        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);

        if (rc == NGX_OK) {

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->upstream->headers_in.headers);

            if (h == NULL) {

                return NGX_ERROR;

            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;

            h->value.len = r->header_end - r->header_start;

            h->key.data = ngx_pnalloc(r->pool,

                                      h->key.len + 1 + h->value.len + 1

                                      + h->key.len);

            if (h->key.data == NULL) {

                return NGX_ERROR;

            }

            h->value.data = h->key.data + h->key.len + 1;

            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;

            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);

            h->key.data[h->key.len] = '\0';

            ngx_memcpy(h->value.data, r->header_start, h->value.len);

            h->value.data[h->value.len] = '\0';

            if (h->key.len == r->lowcase_index) {

                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {

                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);

            }

            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,

                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {

                return NGX_ERROR;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http scgi header: \"%V: %V\"", &h->key, &h->value);

            continue;

        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http scgi header done");

            u = r->upstream;

            if (u->headers_in.status_n) {

                goto done;

            }

            if (u->headers_in.status) {

                status_line = &u->headers_in.status->value;

                status = ngx_atoi(status_line->data, 3);

                if (status == NGX_ERROR) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "upstream sent invalid status \"%V\"",

                                  status_line);

                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;

                }

                u->headers_in.status_n = status;

                u->headers_in.status_line = *status_line;

            } else if (u->headers_in.location) {

                u->headers_in.status_n = 302;

                ngx_str_set(&u->headers_in.status_line,

                            "302 Moved Temporarily");

            } else {

                u->headers_in.status_n = 200;

                ngx_str_set(&u->headers_in.status_line, "200 OK");

            }

            if (u->state && u->state->status == 0) {

                u->state->status = u->headers_in.status_n;

            }

        done:

            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS

                && r->headers_in.upgrade)

            {

                u->upgrade = 1;

            }

            return NGX_OK;

        }

        if (rc == NGX_AGAIN) {

            return NGX_AGAIN;

        }

        /* there was error while a header line parsing */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent invalid header");

        return NGX_HTTP_UPSTREAM_INVALID_HEADER;

    }

}

static void

‌ngx_http_scgi_abort_request(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "abort http scgi request");

    return;

}

static void

‌ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http scgi request");

    return;

}

static void *

‌ngx_http_scgi_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_scgi_main_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

#if (NGX_HTTP_CACHE)

    if (ngx_array_init(&conf->caches, cf->pool, 4,

                       sizeof(ngx_http_file_cache_t *))

        != NGX_OK)

    {

        return NULL;

    }

#endif

    return conf;

}

static void *

‌ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_scgi_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->upstream.store = NGX_CONF_UNSET;

    conf->upstream.store_access = NGX_CONF_UNSET_UINT;

    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

    conf->upstream.buffering = NGX_CONF_UNSET;

    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;

    conf->upstream.force_ranges = NGX_CONF_UNSET;

    conf->upstream.local = NGX_CONF_UNSET_PTR;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;

    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;

    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.pass_request_headers = NGX_CONF_UNSET;

    conf->upstream.pass_request_body = NGX_CONF_UNSET;

#if (NGX_HTTP_CACHE)

    conf->upstream.cache = NGX_CONF_UNSET;

    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;

    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;

    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_lock = NGX_CONF_UNSET;

    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_revalidate = NGX_CONF_UNSET;

#endif

    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.intercept_errors = NGX_CONF_UNSET;

    /* "scgi_cyclic_temp_file" is disabled */

    conf->upstream.cyclic_temp_file = 0;

    conf->upstream.change_buffering = 1;

    ngx_str_set(&conf->upstream.module, "scgi");

    return conf;

}

static char *

‌ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_scgi_loc_conf_t *prev = parent;

    ngx_http_scgi_loc_conf_t *conf = child;

    size_t                        size;

    ngx_int_t                     rc;

    ngx_hash_init_t               hash;

    ngx_http_core_loc_conf_t     *clcf;

#if (NGX_HTTP_CACHE)

    if (conf->upstream.store > 0) {

        conf->upstream.cache = 0;

    }

    if (conf->upstream.cache > 0) {

        conf->upstream.store = 0;

    }

#endif

    if (conf->upstream.store == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);

        conf->upstream.store_lengths = prev->upstream.store_lengths;

        conf->upstream.store_values = prev->upstream.store_values;

    }

    ngx_conf_merge_uint_value(conf->upstream.store_access,

                              prev->upstream.store_access, 0600);

    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,

                              prev->upstream.next_upstream_tries, 0);

    ngx_conf_merge_value(conf->upstream.buffering,

                              prev->upstream.buffering, 1);

    ngx_conf_merge_value(conf->upstream.ignore_client_abort,

                              prev->upstream.ignore_client_abort, 0);

    ngx_conf_merge_value(conf->upstream.force_ranges,

                              prev->upstream.force_ranges, 0);

    ngx_conf_merge_ptr_value(conf->upstream.local,

                              prev->upstream.local, NULL);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,

                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,

                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,

                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,

                              prev->upstream.next_upstream_timeout, 0);

    ngx_conf_merge_size_value(conf->upstream.send_lowat,

                              prev->upstream.send_lowat, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,

                              prev->upstream.buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_size_value(conf->upstream.limit_rate,

                              prev->upstream.limit_rate, 0);

    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,

                              8, ngx_pagesize);

    if (conf->upstream.bufs.num < 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "there must be at least 2 \"scgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    size = conf->upstream.buffer_size;

    if (size < conf->upstream.bufs.size) {

        size = conf->upstream.bufs.size;

    }

    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,

                              prev->upstream.busy_buffers_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.busy_buffers_size = 2 * size;

    } else {

        conf->upstream.busy_buffers_size =

            conf->upstream.busy_buffers_size_conf;

    }

    if (conf->upstream.busy_buffers_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"scgi_busy_buffers_size\" must be equal to or greater "

            "than the maximum of the value of \"scgi_buffer_size\" and "

            "one of the \"scgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    if (conf->upstream.busy_buffers_size

        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"scgi_busy_buffers_size\" must be less than "

            "the size of all \"scgi_buffers\" minus one buffer");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,

                              prev->upstream.temp_file_write_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.temp_file_write_size = 2 * size;

    } else {

        conf->upstream.temp_file_write_size =

            conf->upstream.temp_file_write_size_conf;

    }

    if (conf->upstream.temp_file_write_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"scgi_temp_file_write_size\" must be equal to or greater than "

            "the maximum of the value of \"scgi_buffer_size\" and "

            "one of the \"scgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,

                              prev->upstream.max_temp_file_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;

    } else {

        conf->upstream.max_temp_file_size =

            conf->upstream.max_temp_file_size_conf;

    }

    if (conf->upstream.max_temp_file_size != 0

        && conf->upstream.max_temp_file_size < size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"scgi_max_temp_file_size\" must be equal to zero to disable "

            "temporary files usage or must be equal to or greater than "

            "the maximum of the value of \"scgi_buffer_size\" and "

            "one of the \"scgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,

                                 prev->upstream.ignore_headers,

                                 NGX_CONF_BITMASK_SET);

    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,

                                 prev->upstream.next_upstream,

                                 (NGX_CONF_BITMASK_SET

                                  |NGX_HTTP_UPSTREAM_FT_ERROR

                                  |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET

                                       |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,

                                  prev->upstream.temp_path,

                                  &ngx_http_scgi_temp_path)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.cache,

                              prev->upstream.cache, 0);

        conf->upstream.cache_zone = prev->upstream.cache_zone;

        conf->upstream.cache_value = prev->upstream.cache_value;

    }

    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {

        ngx_shm_zone_t  *shm_zone;

        shm_zone = conf->upstream.cache_zone;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"scgi_cache\" zone \"%V\" is unknown",

                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,

                              prev->upstream.cache_min_uses, 1);

    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,

                              prev->upstream.cache_use_stale,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_OFF));

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET

                                         |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {

        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;

    }

    if (conf->upstream.cache_methods == 0) {

        conf->upstream.cache_methods = prev->upstream.cache_methods;

    }

    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;

    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,

                             prev->upstream.cache_bypass, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.no_cache,

                             prev->upstream.no_cache, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,

                             prev->upstream.cache_valid, NULL);

    if (conf->cache_key.value.data == NULL) {

        conf->cache_key = prev->cache_key;

    }

    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "no \"scgi_cache_key\" for \"scgi_cache\"");

    }

    ngx_conf_merge_value(conf->upstream.cache_lock,

                              prev->upstream.cache_lock, 0);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,

                              prev->upstream.cache_lock_timeout, 5000);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,

                              prev->upstream.cache_lock_age, 5000);

    ngx_conf_merge_value(conf->upstream.cache_revalidate,

                              prev->upstream.cache_revalidate, 0);

#endif

    ngx_conf_merge_value(conf->upstream.pass_request_headers,

                         prev->upstream.pass_request_headers, 1);

    ngx_conf_merge_value(conf->upstream.pass_request_body,

                         prev->upstream.pass_request_body, 1);

    ngx_conf_merge_value(conf->upstream.intercept_errors,

                         prev->upstream.intercept_errors, 0);

    hash.max_size = 512;

    hash.bucket_size = ngx_align(64, ngx_cacheline_size);

    hash.name = "scgi_hide_headers_hash";

    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,

            &prev->upstream, ngx_http_scgi_hide_headers, &hash)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    if (clcf->noname

        && conf->upstream.upstream == NULL && conf->scgi_lengths == NULL)

    {

        conf->upstream.upstream = prev->upstream.upstream;

        conf->scgi_lengths = prev->scgi_lengths;

        conf->scgi_values = prev->scgi_values;

    }

    if (clcf->lmt_excpt && clcf->handler == NULL

        && (conf->upstream.upstream || conf->scgi_lengths))

    {

        clcf->handler = ngx_http_scgi_handler;

    }

    if (conf->params_source == NULL) {

        conf->params = prev->params;

#if (NGX_HTTP_CACHE)

        conf->params_cache = prev->params_cache;

#endif

        conf->params_source = prev->params_source;

    }

    rc = ngx_http_scgi_init_params(cf, conf, &conf->params, NULL);

    if (rc != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache) {

        rc = ngx_http_scgi_init_params(cf, conf, &conf->params_cache,

                                       ngx_http_scgi_cache_headers);

        if (rc != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

#endif

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_scgi_init_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf,

    ngx_http_scgi_params_t *params, ngx_keyval_t *default_params)

{

    u_char                       *p;

    size_t                        size;

    uintptr_t                    *code;

    ngx_uint_t                    i, nsrc;

    ngx_array_t                   headers_names, params_merged;

    ngx_keyval_t                 *h;

    ngx_hash_key_t               *hk;

    ngx_hash_init_t               hash;

    ngx_http_upstream_param_t    *src, *s;

    ngx_http_script_compile_t     sc;

    ngx_http_script_copy_code_t  *copy;

    if (params->hash.buckets) {

        return NGX_OK;

    }

    if (conf->params_source == NULL && default_params == NULL) {

        params->hash.buckets = (void *) 1;

        return NGX_OK;

    }

    params->lengths = ngx_array_create(cf->pool, 64, 1);

    if (params->lengths == NULL) {

        return NGX_ERROR;

    }

    params->values = ngx_array_create(cf->pool, 512, 1);

    if (params->values == NULL) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (conf->params_source) {

        src = conf->params_source->elts;

        nsrc = conf->params_source->nelts;

    } else {

        src = NULL;

        nsrc = 0;

    }

    if (default_params) {

        if (ngx_array_init(&params_merged, cf->temp_pool, 4,

                           sizeof(ngx_http_upstream_param_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        for (i = 0; i < nsrc; i++) {

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = src[i];

        }

        h = default_params;

        while (h->key.len) {

            src = params_merged.elts;

            nsrc = params_merged.nelts;

            for (i = 0; i < nsrc; i++) {

                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {

                    goto next;

                }

            }

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            s->key = h->key;

            s->value = h->value;

            s->skip_empty = 1;

        next:

            h++;

        }

        src = params_merged.elts;

        nsrc = params_merged.nelts;

    }

    for (i = 0; i < nsrc; i++) {

        if (src[i].key.len > sizeof("HTTP_") - 1

            && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)

        {

            hk = ngx_array_push(&headers_names);

            if (hk == NULL) {

                return NGX_ERROR;

            }

            hk->key.len = src[i].key.len - 5;

            hk->key.data = src[i].key.data + 5;

            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);

            hk->value = (void *) 1;

            if (src[i].value.len == 0) {

                continue;

            }

        }

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].key.len + 1;

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].skip_empty;

        size = (sizeof(ngx_http_script_copy_code_t)

                + src[i].key.len + 1 + sizeof(uintptr_t) - 1)

               & ~(sizeof(uintptr_t) - 1);

        copy = ngx_array_push_n(params->values, size);

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = ngx_http_script_copy_code;

        copy->len = src[i].key.len + 1;

        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

        (void) ngx_cpystrn(p, src[i].key.data, src[i].key.len + 1);

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &src[i].value;

        sc.flushes = &params->flushes;

        sc.lengths = &params->lengths;

        sc.values = &params->values;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_ERROR;

        }

        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

        code = ngx_array_push_n(params->values, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) NULL;

    params->number = headers_names.nelts;

    hash.hash = &params->hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = 512;

    hash.bucket_size = 64;

    hash.name = "scgi_params_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);

}

static char *

‌ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_scgi_loc_conf_t *scf = conf;

    ngx_url_t                   u;

    ngx_str_t                  *value, *url;

    ngx_uint_t                  n;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_script_compile_t   sc;

    if (scf->upstream.upstream || scf->scgi_lengths) {

        return "is duplicate";

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_scgi_handler;

    value = cf->args->elts;

    url = &value[1];

    n = ngx_http_script_variables_count(url);

    if (n) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = url;

        sc.lengths = &scf->scgi_lengths;

        sc.values = &scf->scgi_values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];

    u.no_resolve = 1;

    scf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);

    if (scf->upstream.upstream == NULL) {

        return NGX_CONF_ERROR;

    }

    if (clcf->name.data[clcf->name.len - 1] == '/') {

        clcf->auto_redirect = 1;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_scgi_loc_conf_t *scf = conf;

    ngx_str_t                  *value;

    ngx_http_script_compile_t   sc;

    if (scf->upstream.store != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        scf->upstream.store = 0;

        return NGX_CONF_OK;

    }

#if (NGX_HTTP_CACHE)

    if (scf->upstream.cache > 0) {

        return "is incompatible with \"scgi_cache\"";

    }

#endif

    scf->upstream.store = 1;

    if (ngx_strcmp(value[1].data, "on") == 0) {

        return NGX_CONF_OK;

    }

    /* include the terminating '\0' into script */

    value[1].len++;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = &value[1];

    sc.lengths = &scf->upstream.store_lengths;

    sc.values = &scf->upstream.store_values;

    sc.variables = ngx_http_script_variables_count(&value[1]);

    sc.complete_lengths = 1;

    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#if (NGX_HTTP_CACHE)

static char *

‌ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_scgi_loc_conf_t *scf = conf;

    ngx_str_t                         *value;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (scf->upstream.cache != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        scf->upstream.cache = 0;

        return NGX_CONF_OK;

    }

    if (scf->upstream.store > 0) {

        return "is incompatible with \"scgi_store\"";

    }

    scf->upstream.cache = 1;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths != NULL) {

        scf->upstream.cache_value = ngx_palloc(cf->pool,

                                             sizeof(ngx_http_complex_value_t));

        if (scf->upstream.cache_value == NULL) {

            return NGX_CONF_ERROR;

        }

        *scf->upstream.cache_value = cv;

        return NGX_CONF_OK;

    }

    scf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,

                                                     &ngx_http_scgi_module);

    if (scf->upstream.cache_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_scgi_loc_conf_t *scf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (scf->cache_key.value.data) {

        return "is duplicate";

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &scf->cache_key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

src/http/modules/ngx_http_secure_link_module.c - nginx-1.7.9

Global variables defined

ngx_http_secure_link_commands

ngx_http_secure_link_expires_name

ngx_http_secure_link_module

ngx_http_secure_link_module_ctx

ngx_http_secure_link_name

Data types defined

ngx_http_secure_link_conf_t

ngx_http_secure_link_ctx_t

Functions defined

ngx_http_secure_link_add_variables

ngx_http_secure_link_create_conf

ngx_http_secure_link_expires_variable

ngx_http_secure_link_merge_conf

ngx_http_secure_link_old_variable

ngx_http_secure_link_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <ngx_md5.h>

typedef struct {

    ngx_http_complex_value_t  *variable;

    ngx_http_complex_value_t  *md5;

    ngx_str_t                  secret;

‌} ngx_http_secure_link_conf_t;

typedef struct {

    ngx_str_t                  expires;

‌} ngx_http_secure_link_ctx_t;

static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,

    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,

    uintptr_t data);

static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);

static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_secure_link_commands[] = {

    { ngx_string("secure_link"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_set_complex_value_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_secure_link_conf_t, variable),

      NULL },

    { ngx_string("secure_link_md5"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_set_complex_value_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_secure_link_conf_t, md5),

      NULL },

    { ngx_string("secure_link_secret"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_secure_link_conf_t, secret),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_secure_link_module_ctx = {

    ngx_http_secure_link_add_variables,    /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_secure_link_create_conf,      /* create location configuration */

    ngx_http_secure_link_merge_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_secure_link_module = {

    NGX_MODULE_V1,

    &ngx_http_secure_link_module_ctx,      /* module context */

    ngx_http_secure_link_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t  ngx_http_secure_link_name = ngx_string("secure_link");

‌static ngx_str_t  ngx_http_secure_link_expires_name =

    ngx_string("secure_link_expires");

static ngx_int_t

‌ngx_http_secure_link_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char                       *p, *last;

    ngx_str_t                     val, hash;

    time_t                        expires;

    ngx_md5_t                     md5;

    ngx_http_secure_link_ctx_t   *ctx;

    ngx_http_secure_link_conf_t  *conf;

    u_char                        hash_buf[16], md5_buf[16];

    conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);

    if (conf->secret.data) {

        return ngx_http_secure_link_old_variable(r, conf, v, data);

    }

    if (conf->variable == NULL || conf->md5 == NULL) {

        goto not_found;

    }

    if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "secure link: \"%V\"", &val);

    last = val.data + val.len;

    p = ngx_strlchr(val.data, last, ',');

    expires = 0;

    if (p) {

        val.len = p++ - val.data;

        expires = ngx_atotm(p, last - p);

        if (expires <= 0) {

            goto not_found;

        }

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));

        if (ctx == NULL) {

            return NGX_ERROR;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);

        ctx->expires.len = last - p;

        ctx->expires.data = p;

    }

    if (val.len > 24) {

        goto not_found;

    }

    hash.len = 16;

    hash.data = hash_buf;

    if (ngx_decode_base64url(&hash, &val) != NGX_OK) {

        goto not_found;

    }

    if (hash.len != 16) {

        goto not_found;

    }

    if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "secure link md5: \"%V\"", &val);

    ngx_md5_init(&md5);

    ngx_md5_update(&md5, val.data, val.len);

    ngx_md5_final(md5_buf, &md5);

    if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {

        goto not_found;

    }

    v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");

    v->len = 1;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_secure_link_old_variable(ngx_http_request_t *r,

    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,

    uintptr_t data)

{

    u_char      *p, *start, *end, *last;

    size_t       len;

    ngx_int_t    n;

    ngx_uint_t   i;

    ngx_md5_t    md5;

    u_char       hash[16];

    p = &r->unparsed_uri.data[1];

    last = r->unparsed_uri.data + r->unparsed_uri.len;

    while (p < last) {

        if (*p++ == '/') {

            start = p;

            goto md5_start;

        }

    }

    goto not_found;

md5_start:

    while (p < last) {

        if (*p++ == '/') {

            end = p - 1;

            goto url_start;

        }

    }

    goto not_found;

url_start:

    len = last - p;

    if (end - start != 32 || len == 0) {

        goto not_found;

    }

    ngx_md5_init(&md5);

    ngx_md5_update(&md5, p, len);

    ngx_md5_update(&md5, conf->secret.data, conf->secret.len);

    ngx_md5_final(hash, &md5);

    for (i = 0; i < 16; i++) {

        n = ngx_hextoi(&start[2 * i], 2);

        if (n == NGX_ERROR || n != hash[i]) {

            goto not_found;

        }

    }

    v->len = len;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_secure_link_expires_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_secure_link_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);

    if (ctx) {

        v->len = ctx->expires.len;

        v->valid = 1;

        v->no_cacheable = 0;

        v->not_found = 0;

        v->data = ctx->expires.data;

    } else {

        v->not_found = 1;

    }

    return NGX_OK;

}

static void *

‌ngx_http_secure_link_create_conf(ngx_conf_t *cf)

{

    ngx_http_secure_link_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->variable = NULL;

     *     conf->md5 = NULL;

     *     conf->secret = { 0, NULL };

     */

    return conf;

}

static char *

‌ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_secure_link_conf_t *prev = parent;

    ngx_http_secure_link_conf_t *conf = child;

    if (conf->secret.data) {

        if (conf->variable || conf->md5) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "\"secure_link_secret\" cannot be mixed with "

                               "\"secure_link\" and \"secure_link_md5\"");

            return NGX_CONF_ERROR;

        }

        return NGX_CONF_OK;

    }

    if (conf->variable == NULL) {

        conf->variable = prev->variable;

    }

    if (conf->md5 == NULL) {

        conf->md5 = prev->md5;

    }

    if (conf->variable == NULL && conf->md5 == NULL) {

        conf->secret = prev->secret;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_secure_link_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var;

    var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_secure_link_variable;

    var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);

    if (var == NULL) {

        return NGX_ERROR;

    }

    var->get_handler = ngx_http_secure_link_expires_variable;

    return NGX_OK;

}

src/http/modules/ngx_http_split_clients_module.c - nginx-1.7.9

Global variables defined

ngx_http_split_clients_commands

ngx_http_split_clients_module

ngx_http_split_clients_module_ctx

Data types defined

ngx_http_split_clients_ctx_t

ngx_http_split_clients_part_t

Functions defined

ngx_conf_split_clients_block

ngx_http_split_clients

ngx_http_split_clients_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    uint32_t                    percent;

    ngx_http_variable_value_t   value;

‌} ngx_http_split_clients_part_t;

typedef struct {

    ngx_http_complex_value_t    value;

    ngx_array_t                 parts;

‌} ngx_http_split_clients_ctx_t;

static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,

    void *conf);

‌static ngx_command_t  ngx_http_split_clients_commands[] = {

    { ngx_string("split_clients"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,

      ngx_conf_split_clients_block,

      NGX_HTTP_MAIN_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_split_clients_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* 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 */

};

‌ngx_module_t  ngx_http_split_clients_module = {

    NGX_MODULE_V1,

    &ngx_http_split_clients_module_ctx,    /* module context */

    ngx_http_split_clients_commands,       /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_split_clients_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;

    uint32_t                        hash;

    ngx_str_t                       val;

    ngx_uint_t                      i;

    ngx_http_split_clients_part_t  *part;

    *v = ngx_http_variable_null_value;

    if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {

        return NGX_OK;

    }

    hash = ngx_murmur_hash2(val.data, val.len);

    part = ctx->parts.elts;

    for (i = 0; i < ctx->parts.nelts; i++) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "http split: %uD %uD", hash, part[i].percent);

        if (hash < part[i].percent || part[i].percent == 0) {

            *v = part[i].value;

            return NGX_OK;

        }

    }

    return NGX_OK;

}

static char *

‌ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                                *rv;

    uint32_t                             sum, last;

    ngx_str_t                           *value, name;

    ngx_uint_t                           i;

    ngx_conf_t                           save;

    ngx_http_variable_t                 *var;

    ngx_http_split_clients_ctx_t        *ctx;

    ngx_http_split_clients_part_t       *part;

    ngx_http_compile_complex_value_t     ccv;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &ctx->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    name = value[2];

    if (name.data[0] != '$') {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid variable name \"%V\"", &name);

        return NGX_CONF_ERROR;

    }

    name.len--;

    name.data++;

    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);

    if (var == NULL) {

        return NGX_CONF_ERROR;

    }

    var->get_handler = ngx_http_split_clients_variable;

    var->data = (uintptr_t) ctx;

    if (ngx_array_init(&ctx->parts, cf->pool, 2,

                       sizeof(ngx_http_split_clients_part_t))

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    save = *cf;

    cf->ctx = ctx;

    cf->handler = ngx_http_split_clients;

    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    if (rv != NGX_CONF_OK) {

        return rv;

    }

    sum = 0;

    last = 0;

    part = ctx->parts.elts;

    for (i = 0; i < ctx->parts.nelts; i++) {

        sum = part[i].percent ? sum + part[i].percent : 10000;

        if (sum > 10000) {

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                               "percent total is greater than 100%%");

            return NGX_CONF_ERROR;

        }

        if (part[i].percent) {

            last += part[i].percent * (uint64_t) 0xffffffff / 10000;

            part[i].percent = last;

        }

    }

    return rv;

}

static char *

‌ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)

{

    ngx_int_t                       n;

    ngx_str_t                      *value;

    ngx_http_split_clients_ctx_t   *ctx;

    ngx_http_split_clients_part_t  *part;

    ctx = cf->ctx;

    value = cf->args->elts;

    part = ngx_array_push(&ctx->parts);

    if (part == NULL) {

        return NGX_CONF_ERROR;

    }

    if (value[0].len == 1 && value[0].data[0] == '*') {

        part->percent = 0;

    } else {

        if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {

            goto invalid;

        }

        n = ngx_atofp(value[0].data, value[0].len - 1, 2);

        if (n == NGX_ERROR || n == 0) {

            goto invalid;

        }

        part->percent = (uint32_t) n;

    }

    part->value.len = value[1].len;

    part->value.valid = 1;

    part->value.no_cacheable = 0;

    part->value.not_found = 0;

    part->value.data = value[1].data;

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                       "invalid percent value \"%V\"", &value[0]);

    return NGX_CONF_ERROR;

}

src/http/modules/ngx_http_static_module.c - nginx-1.7.9

Global variables defined

ngx_http_static_module

ngx_http_static_module_ctx

Functions defined

ngx_http_static_handler

ngx_http_static_init

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_static_init(ngx_conf_t *cf);

‌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 */

};

‌ngx_module_t  ngx_http_static_module = {

    NGX_MODULE_V1,

    &ngx_http_static_module_ctx,           /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

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_etag(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    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);

}

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;

}

src/http/modules/ngx_http_stub_status_module.c - nginx-1.7.9

Global variables defined

ngx_http_status_commands

ngx_http_stub_status_module

ngx_http_stub_status_module_ctx

ngx_http_stub_status_vars

Functions defined

ngx_http_set_stub_status

ngx_http_stub_status_add_variables

ngx_http_stub_status_handler

ngx_http_stub_status_variable

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

static ngx_int_t ngx_http_stub_status_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_stub_status_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data);

static ngx_int_t ngx_http_stub_status_add_variables(ngx_conf_t *cf);

static char *ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_status_commands[] = {

    { ngx_string("stub_status"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,

      ngx_http_set_stub_status,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_stub_status_module_ctx = {

    ngx_http_stub_status_add_variables,    /* preconfiguration */

    NULL,                                  /* 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 */

};

‌ngx_module_t  ngx_http_stub_status_module = {

    NGX_MODULE_V1,

    &ngx_http_stub_status_module_ctx,      /* module context */

    ngx_http_status_commands,              /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_variable_t  ngx_http_stub_status_vars[] = {

    { ngx_string("connections_active"), NULL, ngx_http_stub_status_variable,

      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("connections_reading"), NULL, ngx_http_stub_status_variable,

      1, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("connections_writing"), NULL, ngx_http_stub_status_variable,

      2, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_string("connections_waiting"), NULL, ngx_http_stub_status_variable,

      3, NGX_HTTP_VAR_NOCACHEABLE, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }

};

static ngx_int_t

‌ngx_http_stub_status_handler(ngx_http_request_t *r)

{

    size_t             size;

    ngx_int_t          rc;

    ngx_buf_t         *b;

    ngx_chain_t        out;

    ngx_atomic_int_t   ap, hn, ac, rq, rd, wr, wa;

    if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {

        return NGX_HTTP_NOT_ALLOWED;

    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {

        return rc;

    }

    r->headers_out.content_type_len = sizeof("text/plain") - 1;

    ngx_str_set(&r->headers_out.content_type, "text/plain");

    r->headers_out.content_type_lowcase = NULL;

    if (r->method == NGX_HTTP_HEAD) {

        r->headers_out.status = NGX_HTTP_OK;

        rc = ngx_http_send_header(r);

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

            return rc;

        }

    }

    size = sizeof("Active connections:  \n") + NGX_ATOMIC_T_LEN

           + sizeof("server accepts handled requests\n") - 1

           + 6 + 3 * NGX_ATOMIC_T_LEN

           + sizeof("Reading:  Writing:  Waiting:  \n") + 3 * NGX_ATOMIC_T_LEN;

    b = ngx_create_temp_buf(r->pool, size);

    if (b == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    out.buf = b;

    out.next = NULL;

    ap = *ngx_stat_accepted;

    hn = *ngx_stat_handled;

    ac = *ngx_stat_active;

    rq = *ngx_stat_requests;

    rd = *ngx_stat_reading;

    wr = *ngx_stat_writing;

    wa = *ngx_stat_waiting;

    b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac);

    b->last = ngx_cpymem(b->last, "server accepts handled requests\n",

                         sizeof("server accepts handled requests\n") - 1);

    b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq);

    b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n",

                          rd, wr, wa);

    r->headers_out.status = NGX_HTTP_OK;

    r->headers_out.content_length_n = b->last - b->pos;

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

    b->last_in_chain = 1;

    rc = ngx_http_send_header(r);

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

        return rc;

    }

    return ngx_http_output_filter(r, &out);

}

static ngx_int_t

‌ngx_http_stub_status_variable(ngx_http_request_t *r,

    ngx_http_variable_value_t *v, uintptr_t data)

{

    u_char            *p;

    ngx_atomic_int_t   value;

    p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);

    if (p == NULL) {

        return NGX_ERROR;

    }

    switch (data) {

    case 0:

        value = *ngx_stat_active;

        break;

    case 1:

        value = *ngx_stat_reading;

        break;

    case 2:

        value = *ngx_stat_writing;

        break;

    case 3:

        value = *ngx_stat_waiting;

        break;

    /* suppress warning */

    default:

        value = 0;

        break;

    }

    v->len = ngx_sprintf(p, "%uA", value) - p;

    v->valid = 1;

    v->no_cacheable = 0;

    v->not_found = 0;

    v->data = p;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_stub_status_add_variables(ngx_conf_t *cf)

{

    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_stub_status_vars; v->name.len; v++) {

        var = ngx_http_add_variable(cf, &v->name, v->flags);

        if (var == NULL) {

            return NGX_ERROR;

        }

        var->get_handler = v->get_handler;

        var->data = v->data;

    }

    return NGX_OK;

}

static char *

‌ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_stub_status_handler;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_sub_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_body_filter

ngx_http_next_header_filter

ngx_http_sub_filter_commands

ngx_http_sub_filter_module

ngx_http_sub_filter_module_ctx

Data types defined

ngx_http_sub_ctx_t

ngx_http_sub_loc_conf_t

ngx_http_sub_state_e

Functions defined

ngx_http_sub_body_filter

ngx_http_sub_create_conf

ngx_http_sub_filter

ngx_http_sub_filter_init

ngx_http_sub_header_filter

ngx_http_sub_merge_conf

ngx_http_sub_output

ngx_http_sub_parse

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_str_t                  match;

    ngx_http_complex_value_t   value;

    ngx_hash_t                 types;

    ngx_flag_t                 once;

    ngx_flag_t                 last_modified;

    ngx_array_t               *types_keys;

‌} ngx_http_sub_loc_conf_t;

typedef enum {

    sub_start_state = 0,

    sub_match_state,

‌} ngx_http_sub_state_e;

typedef struct {

    ngx_str_t                  match;

    ngx_str_t                  saved;

    ngx_str_t                  looked;

    ngx_uint_t                 once;   /* unsigned  once:1 */

    ngx_buf_t                 *buf;

    u_char                    *pos;

    u_char                    *copy_start;

    u_char                    *copy_end;

    ngx_chain_t               *in;

    ngx_chain_t               *out;

    ngx_chain_t              **last_out;

    ngx_chain_t               *busy;

    ngx_chain_t               *free;

    ngx_str_t                  sub;

    ngx_uint_t                 state;

‌} ngx_http_sub_ctx_t;

static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,

    ngx_http_sub_ctx_t *ctx);

static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,

    ngx_http_sub_ctx_t *ctx);

static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static void *ngx_http_sub_create_conf(ngx_conf_t *cf);

static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_sub_filter_commands[] = {

    { ngx_string("sub_filter"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_http_sub_filter,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("sub_filter_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_sub_loc_conf_t, types_keys),

      &ngx_http_html_default_types[0] },

    { ngx_string("sub_filter_once"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_sub_loc_conf_t, once),

      NULL },

    { ngx_string("sub_filter_last_modified"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_sub_loc_conf_t, last_modified),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_sub_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_sub_filter_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_sub_create_conf,              /* create location configuration */

    ngx_http_sub_merge_conf                /* merge location configuration */

};

‌ngx_module_t  ngx_http_sub_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_sub_filter_module_ctx,       /* module context */

    ngx_http_sub_filter_commands,          /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_sub_header_filter(ngx_http_request_t *r)

{

    ngx_http_sub_ctx_t        *ctx;

    ngx_http_sub_loc_conf_t  *slcf;

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);

    if (slcf->match.len == 0

        || r->headers_out.content_length_n == 0

        || ngx_http_test_content_type(r, &slcf->types) == NULL)

    {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len);

    if (ctx->saved.data == NULL) {

        return NGX_ERROR;

    }

    ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len);

    if (ctx->looked.data == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);

    ctx->match = slcf->match;

    ctx->last_out = &ctx->out;

    r->filter_need_in_memory = 1;

    if (r == r->main) {

        ngx_http_clear_content_length(r);

        if (!slcf->last_modified) {

            ngx_http_clear_last_modified(r);

            ngx_http_clear_etag(r);

        } else {

            ngx_http_weak_etag(r);

        }

    }

    return ngx_http_next_header_filter(r);

}

static ngx_int_t

‌ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_int_t                  rc;

    ngx_buf_t                 *b;

    ngx_chain_t               *cl;

    ngx_http_sub_ctx_t        *ctx;

    ngx_http_sub_loc_conf_t   *slcf;

    ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);

    if (ctx == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    if ((in == NULL

         && ctx->buf == NULL

         && ctx->in == NULL

         && ctx->busy == NULL))

    {

        return ngx_http_next_body_filter(r, in);

    }

    if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {

        if (ctx->busy) {

            if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {

                return NGX_ERROR;

            }

        }

        return ngx_http_next_body_filter(r, in);

    }

    /* add the incoming chain to the chain ctx->in */

    if (in) {

        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http sub filter \"%V\"", &r->uri);

    while (ctx->in || ctx->buf) {

        if (ctx->buf == NULL) {

            ctx->buf = ctx->in->buf;

            ctx->in = ctx->in->next;

            ctx->pos = ctx->buf->pos;

        }

        if (ctx->state == sub_start_state) {

            ctx->copy_start = ctx->pos;

            ctx->copy_end = ctx->pos;

        }

        b = NULL;

        while (ctx->pos < ctx->buf->last) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "saved: \"%V\" state: %d", &ctx->saved, ctx->state);

            rc = ngx_http_sub_parse(r, ctx);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "parse: %d, looked: \"%V\" %p-%p",

                           rc, &ctx->looked, ctx->copy_start, ctx->copy_end);

            if (rc == NGX_ERROR) {

                return rc;

            }

            if (ctx->saved.len) {

                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                               "saved: \"%V\"", &ctx->saved);

                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);

                if (cl == NULL) {

                    return NGX_ERROR;

                }

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->pos = ngx_pnalloc(r->pool, ctx->saved.len);

                if (b->pos == NULL) {

                    return NGX_ERROR;

                }

                ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);

                b->last = b->pos + ctx->saved.len;

                b->memory = 1;

                *ctx->last_out = cl;

                ctx->last_out = &cl->next;

                ctx->saved.len = 0;

            }

            if (ctx->copy_start != ctx->copy_end) {

                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);

                if (cl == NULL) {

                    return NGX_ERROR;

                }

                b = cl->buf;

                ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

                b->pos = ctx->copy_start;

                b->last = ctx->copy_end;

                b->shadow = NULL;

                b->last_buf = 0;

                b->last_in_chain = 0;

                b->recycled = 0;

                if (b->in_file) {

                    b->file_last = b->file_pos + (b->last - ctx->buf->pos);

                    b->file_pos += b->pos - ctx->buf->pos;

                }

                *ctx->last_out = cl;

                ctx->last_out = &cl->next;

            }

            if (ctx->state == sub_start_state) {

                ctx->copy_start = ctx->pos;

                ctx->copy_end = ctx->pos;

            } else {

                ctx->copy_start = NULL;

                ctx->copy_end = NULL;

            }

            if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {

                ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);

                ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);

            }

            if (rc == NGX_AGAIN) {

                continue;

            }

            /* rc == NGX_OK */

            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);

            if (cl == NULL) {

                return NGX_ERROR;

            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);

            if (ctx->sub.data == NULL) {

                if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)

                    != NGX_OK)

                {

                    return NGX_ERROR;

                }

            }

            if (ctx->sub.len) {

                b->memory = 1;

                b->pos = ctx->sub.data;

                b->last = ctx->sub.data + ctx->sub.len;

            } else {

                b->sync = 1;

            }

            *ctx->last_out = cl;

            ctx->last_out = &cl->next;

            ctx->once = slcf->once;

            continue;

        }

        if (ctx->looked.len

            && (ctx->buf->last_buf || ctx->buf->last_in_chain))

        {

            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);

            if (cl == NULL) {

                return NGX_ERROR;

            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->pos = ctx->looked.data;

            b->last = b->pos + ctx->looked.len;

            b->memory = 1;

            *ctx->last_out = cl;

            ctx->last_out = &cl->next;

            ctx->looked.len = 0;

        }

        if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync

            || ngx_buf_in_memory(ctx->buf))

        {

            if (b == NULL) {

                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);

                if (cl == NULL) {

                    return NGX_ERROR;

                }

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->sync = 1;

                *ctx->last_out = cl;

                ctx->last_out = &cl->next;

            }

            b->last_buf = ctx->buf->last_buf;

            b->last_in_chain = ctx->buf->last_in_chain;

            b->flush = ctx->buf->flush;

            b->shadow = ctx->buf;

            b->recycled = ctx->buf->recycled;

        }

        ctx->buf = NULL;

        ctx->saved.len = ctx->looked.len;

        ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);

    }

    if (ctx->out == NULL && ctx->busy == NULL) {

        return NGX_OK;

    }

    return ngx_http_sub_output(r, ctx);

}

static ngx_int_t

‌ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)

{

    ngx_int_t     rc;

    ngx_buf_t    *b;

    ngx_chain_t  *cl;

#if 1

    b = NULL;

    for (cl = ctx->out; cl; cl = cl->next) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "sub out: %p %p", cl->buf, cl->buf->pos);

        if (cl->buf == b) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,

                          "the same buf was used in sub");

            ngx_debug_point();

            return NGX_ERROR;

        }

        b = cl->buf;

    }

#endif

    rc = ngx_http_next_body_filter(r, ctx->out);

    if (ctx->busy == NULL) {

        ctx->busy = ctx->out;

    } else {

        for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }

        cl->next = ctx->out;

    }

    ctx->out = NULL;

    ctx->last_out = &ctx->out;

    while (ctx->busy) {

        cl = ctx->busy;

        b = cl->buf;

        if (ngx_buf_size(b) != 0) {

            break;

        }

        if (b->shadow) {

            b->shadow->pos = b->shadow->last;

        }

        ctx->busy = cl->next;

        if (ngx_buf_in_memory(b) || b->in_file) {

            /* add data bufs only to the free buf chain */

            cl->next = ctx->free;

            ctx->free = cl;

        }

    }

    if (ctx->in || ctx->buf) {

        r->buffered |= NGX_HTTP_SUB_BUFFERED;

    } else {

        r->buffered &= ~NGX_HTTP_SUB_BUFFERED;

    }

    return rc;

}

static ngx_int_t

‌ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)

{

    u_char                *p, *last, *copy_end, ch, match;

    size_t                 looked, i;

    ngx_http_sub_state_e   state;

    if (ctx->once) {

        ctx->copy_start = ctx->pos;

        ctx->copy_end = ctx->buf->last;

        ctx->pos = ctx->buf->last;

        ctx->looked.len = 0;

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once");

        return NGX_AGAIN;

    }

    state = ctx->state;

    looked = ctx->looked.len;

    last = ctx->buf->last;

    copy_end = ctx->copy_end;

    for (p = ctx->pos; p < last; p++) {

        ch = *p;

        ch = ngx_tolower(ch);

        if (state == sub_start_state) {

            /* the tight loop */

            match = ctx->match.data[0];

            for ( ;; ) {

                if (ch == match) {

                    if (ctx->match.len == 1) {

                        ctx->pos = p + 1;

                        ctx->copy_end = p;

                        return NGX_OK;

                    }

                    copy_end = p;

                    ctx->looked.data[0] = *p;

                    looked = 1;

                    state = sub_match_state;

                    goto match_started;

                }

                if (++p == last) {

                    break;

                }

                ch = *p;

                ch = ngx_tolower(ch);

            }

            ctx->state = state;

            ctx->pos = p;

            ctx->looked.len = looked;

            ctx->copy_end = p;

            if (ctx->copy_start == NULL) {

                ctx->copy_start = ctx->buf->pos;

            }

            return NGX_AGAIN;

        match_started:

            continue;

        }

        /* state == sub_match_state */

        if (ch == ctx->match.data[looked]) {

            ctx->looked.data[looked] = *p;

            looked++;

            if (looked == ctx->match.len) {

                ctx->state = sub_start_state;

                ctx->pos = p + 1;

                ctx->looked.len = 0;

                ctx->saved.len = 0;

                ctx->copy_end = copy_end;

                if (ctx->copy_start == NULL && copy_end) {

                    ctx->copy_start = ctx->buf->pos;

                }

                return NGX_OK;

            }

        } else {

            /*

             * check if there is another partial match in previously

             * matched substring to catch cases like "aab" in "aaab"

             */

            ctx->looked.data[looked] = *p;

            looked++;

            for (i = 1; i < looked; i++) {

                if (ngx_strncasecmp(ctx->looked.data + i,

                                    ctx->match.data, looked - i)

                    == 0)

                {

                    break;

                }

            }

            if (i < looked) {

                if (ctx->saved.len > i) {

                    ctx->saved.len = i;

                }

                if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) {

                    copy_end = p + 1 - (looked - i);

                }

                ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i);

                looked = looked - i;

            } else {

                copy_end = p;

                looked = 0;

                state = sub_start_state;

            }

            if (ctx->saved.len) {

                p++;

                goto out;

            }

        }

    }

    ctx->saved.len = 0;

out:

    ctx->state = state;

    ctx->pos = p;

    ctx->looked.len = looked;

    ctx->copy_end = (state == sub_start_state) ? p : copy_end;

    if (ctx->copy_start == NULL && ctx->copy_end) {

        ctx->copy_start = ctx->buf->pos;

    }

    return NGX_AGAIN;

}

static char *

‌ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_sub_loc_conf_t *slcf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    if (slcf->match.data) {

        return "is duplicate";

    }

    value = cf->args->elts;

    ngx_strlow(value[1].data, value[1].data, value[1].len);

    slcf->match = value[1];

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &slcf->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static void *

‌ngx_http_sub_create_conf(ngx_conf_t *cf)

{

    ngx_http_sub_loc_conf_t  *slcf;

    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));

    if (slcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->match = { 0, NULL };

     *     conf->types = { NULL };

     *     conf->types_keys = NULL;

     */

    slcf->once = NGX_CONF_UNSET;

    slcf->last_modified = NGX_CONF_UNSET;

    return slcf;

}

static char *

‌ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_sub_loc_conf_t *prev = parent;

    ngx_http_sub_loc_conf_t *conf = child;

    ngx_conf_merge_value(conf->once, prev->once, 1);

    ngx_conf_merge_str_value(conf->match, prev->match, "");

    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

    if (conf->value.value.data == NULL) {

        conf->value = prev->value;

    }

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_html_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_sub_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_sub_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_sub_body_filter;

    return NGX_OK;

}

src/http/modules/ngx_http_upstream_hash_module.c - nginx-1.7.9

Global variables defined

ngx_http_upstream_hash_commands

ngx_http_upstream_hash_module

ngx_http_upstream_hash_module_ctx

Data types defined

ngx_http_upstream_chash_point_t

ngx_http_upstream_chash_points_t

ngx_http_upstream_hash_peer_data_t

ngx_http_upstream_hash_srv_conf_t

Functions defined

ngx_http_upstream_add_chash_point

ngx_http_upstream_find_chash_point

ngx_http_upstream_get_chash_peer

ngx_http_upstream_get_hash_peer

ngx_http_upstream_hash

ngx_http_upstream_hash_create_conf

ngx_http_upstream_init_chash

ngx_http_upstream_init_chash_peer

ngx_http_upstream_init_hash

ngx_http_upstream_init_hash_peer

Source code

/*

* Copyright (C) Roman Arutyunyan

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    uint32_t                            hash;

    ngx_str_t                          *server;

‌} ngx_http_upstream_chash_point_t;

typedef struct {

    ngx_uint_t                          number;

    ngx_http_upstream_chash_point_t     point[1];

‌} ngx_http_upstream_chash_points_t;

typedef struct {

    ngx_http_complex_value_t            key;

    ngx_http_upstream_chash_points_t   *points;

‌} ngx_http_upstream_hash_srv_conf_t;

typedef struct {

    /* the round robin data must be first */

    ngx_http_upstream_rr_peer_data_t    rrp;

    ngx_http_upstream_hash_srv_conf_t  *conf;

    ngx_str_t                           key;

    ngx_uint_t                          tries;

    ngx_uint_t                          rehash;

    uint32_t                            hash;

    ngx_event_get_peer_pt               get_rr_peer;

‌} ngx_http_upstream_hash_peer_data_t;

static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,

    void *data);

static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us);

static void ngx_http_upstream_add_chash_point(

    ngx_http_upstream_chash_points_t *points, uint32_t hash, ngx_str_t *server);

static ngx_uint_t ngx_http_upstream_find_chash_point(

    ngx_http_upstream_chash_points_t *points, uint32_t hash);

static ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,

    void *data);

static void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf);

static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_upstream_hash_commands[] = {

    { ngx_string("hash"),

      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,

      ngx_http_upstream_hash,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_upstream_hash_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_http_upstream_hash_create_conf,    /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_upstream_hash_module = {

    NGX_MODULE_V1,

    &ngx_http_upstream_hash_module_ctx,    /* module context */

    ngx_http_upstream_hash_commands,       /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)

{

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {

        return NGX_ERROR;

    }

    us->peer.init = ngx_http_upstream_init_hash_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_http_upstream_hash_srv_conf_t   *hcf;

    ngx_http_upstream_hash_peer_data_t  *hp;

    hp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t));

    if (hp == NULL) {

        return NGX_ERROR;

    }

    r->upstream->peer.data = &hp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {

        return NGX_ERROR;

    }

    r->upstream->peer.get = ngx_http_upstream_get_hash_peer;

    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

    if (ngx_http_complex_value(r, &hcf->key, &hp->key) != NGX_OK) {

        return NGX_ERROR;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "upstream hash key:\"%V\"", &hp->key);

    hp->conf = hcf;

    hp->tries = 0;

    hp->rehash = 0;

    hp->hash = 0;

    hp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_hash_peer_data_t  *hp = data;

    time_t                        now;

    u_char                        buf[NGX_INT_T_LEN];

    size_t                        size;

    uint32_t                      hash;

    ngx_int_t                     w;

    uintptr_t                     m;

    ngx_uint_t                    i, n, p;

    ngx_http_upstream_rr_peer_t  *peer;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get hash peer, try: %ui", pc->tries);

    if (hp->tries > 20 || hp->rrp.peers->single) {

        return hp->get_rr_peer(pc, &hp->rrp);

    }

    now = ngx_time();

    pc->cached = 0;

    pc->connection = NULL;

    for ( ;; ) {

        /*

         * Hash expression is compatible with Cache::Memcached:

         * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH

         * with REHASH omitted at the first iteration.

         */

        ngx_crc32_init(hash);

        if (hp->rehash > 0) {

            size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;

            ngx_crc32_update(&hash, buf, size);

        }

        ngx_crc32_update(&hash, hp->key.data, hp->key.len);

        ngx_crc32_final(hash);

        hash = (hash >> 16) & 0x7fff;

        hp->hash += hash;

        hp->rehash++;

        if (!hp->rrp.peers->weighted) {

            p = hp->hash % hp->rrp.peers->number;

        } else {

            w = hp->hash % hp->rrp.peers->total_weight;

            for (i = 0; i < hp->rrp.peers->number; i++) {

                w -= hp->rrp.peers->peer[i].weight;

                if (w < 0) {

                    break;

                }

            }

            p = i;

        }

        n = p / (8 * sizeof(uintptr_t));

        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

        if (hp->rrp.tried[n] & m) {

            goto next;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get hash peer, value:%uD, peer:%ui", hp->hash, p);

        peer = &hp->rrp.peers->peer[p];

        if (peer->down) {

            goto next;

        }

        if (peer->max_fails

            && peer->fails >= peer->max_fails

            && now - peer->checked <= peer->fail_timeout)

        {

            goto next;

        }

        break;

    next:

        if (++hp->tries > 20) {

            return hp->get_rr_peer(pc, &hp->rrp);

        }

    }

    hp->rrp.current = p;

    pc->sockaddr = peer->sockaddr;

    pc->socklen = peer->socklen;

    pc->name = &peer->name;

    if (now - peer->checked > peer->fail_timeout) {

        peer->checked = now;

    }

    hp->rrp.tried[n] |= m;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)

{

    u_char                             *host, *port, c;

    size_t                              host_len, port_len, size;

    uint32_t                            hash, base_hash, prev_hash;

    ngx_str_t                          *server;

    ngx_uint_t                          npoints, i, j;

    ngx_http_upstream_rr_peer_t        *peer;

    ngx_http_upstream_rr_peers_t       *peers;

    ngx_http_upstream_chash_points_t   *points;

    ngx_http_upstream_hash_srv_conf_t  *hcf;

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {

        return NGX_ERROR;

    }

    us->peer.init = ngx_http_upstream_init_chash_peer;

    peers = us->peer.data;

    npoints = peers->total_weight * 160;

    size = sizeof(ngx_http_upstream_chash_points_t)

           + sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1);

    points = ngx_palloc(cf->pool, size);

    if (points == NULL) {

        return NGX_ERROR;

    }

    points->number = 0;

    for (i = 0; i < peers->number; i++) {

        peer = &peers->peer[i];

        server = &peer->server;

        /*

         * Hash expression is compatible with Cache::Memcached::Fast:

         * crc32(HOST \0 PORT PREV_HASH).

         */

        if (server->len >= 5

            && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)

        {

            host = server->data + 5;

            host_len = server->len - 5;

            port = NULL;

            port_len = 0;

            goto done;

        }

        for (j = 0; j < server->len; j++) {

            c = server->data[server->len - j - 1];

            if (c == ':') {

                host = server->data;

                host_len = server->len - j - 1;

                port = server->data + server->len - j;

                port_len = j;

                goto done;

            }

            if (c < '0' || c > '9') {

                break;

            }

        }

        host = server->data;

        host_len = server->len;

        port = NULL;

        port_len = 0;

    done:

        ngx_crc32_init(base_hash);

        ngx_crc32_update(&base_hash, host, host_len);

        ngx_crc32_update(&base_hash, (u_char *) "", 1);

        ngx_crc32_update(&base_hash, port, port_len);

        prev_hash = 0;

        npoints = peer->weight * 160;

        for (j = 0; j < npoints; j++) {

            hash = base_hash;

            ngx_crc32_update(&hash, (u_char *) &prev_hash, sizeof(uint32_t));

            ngx_crc32_final(hash);

            ngx_http_upstream_add_chash_point(points, hash, &peer->server);

            prev_hash = hash;

        }

    }

    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

    hcf->points = points;

    return NGX_OK;

}

static void

‌ngx_http_upstream_add_chash_point(ngx_http_upstream_chash_points_t *points,

    uint32_t hash, ngx_str_t *server)

{

    size_t                            size;

    ngx_uint_t                        i;

    ngx_http_upstream_chash_point_t  *point;

    i = ngx_http_upstream_find_chash_point(points, hash);

    point = &points->point[i];

    if (point->hash == hash) {

        return;

    }

    size = (points->number - i) * sizeof(ngx_http_upstream_chash_point_t);

    ngx_memmove(point + 1, point, size);

    points->number++;

    point->hash = hash;

    point->server = server;

}

static ngx_uint_t

‌ngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,

    uint32_t hash)

{

    ngx_uint_t                        i, j, k;

    ngx_http_upstream_chash_point_t  *point;

    /* find first point >= hash */

    point = &points->point[0];

    i = 0;

    j = points->number;

    while (i < j) {

        k = (i + j) / 2;

        if (hash > point[k].hash) {

            i = k + 1;

        } else if (hash < point[k].hash) {

            j = k;

        } else {

            return k;

        }

    }

    return i;

}

static ngx_int_t

‌ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    uint32_t                             hash;

    ngx_http_upstream_hash_srv_conf_t   *hcf;

    ngx_http_upstream_hash_peer_data_t  *hp;

    if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {

        return NGX_ERROR;

    }

    r->upstream->peer.get = ngx_http_upstream_get_chash_peer;

    hp = r->upstream->peer.data;

    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

    hash = ngx_crc32_long(hp->key.data, hp->key.len);

    hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_hash_peer_data_t  *hp = data;

    time_t                              now;

    intptr_t                            m;

    ngx_str_t                          *server;

    ngx_int_t                           total;

    ngx_uint_t                          i, n;

    ngx_http_upstream_rr_peer_t        *peer, *best;

    ngx_http_upstream_chash_point_t    *point;

    ngx_http_upstream_chash_points_t   *points;

    ngx_http_upstream_hash_srv_conf_t  *hcf;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get consistent hash peer, try: %ui", pc->tries);

    pc->cached = 0;

    pc->connection = NULL;

    now = ngx_time();

    hcf = hp->conf;

    points = hcf->points;

    point = &points->point[0];

    for ( ;; ) {

        server = point[hp->hash % points->number].server;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "consistent hash peer:%uD, server:\"%V\"",

                       hp->hash, server);

        best = NULL;

        total = 0;

        for (i = 0; i < hp->rrp.peers->number; i++) {

            n = i / (8 * sizeof(uintptr_t));

            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

            if (hp->rrp.tried[n] & m) {

                continue;

            }

            peer = &hp->rrp.peers->peer[i];

            if (peer->down) {

                continue;

            }

            if (peer->server.len != server->len

                || ngx_strncmp(peer->server.data, server->data, server->len)

                   != 0)

            {

                continue;

            }

            if (peer->max_fails

                && peer->fails >= peer->max_fails

                && now - peer->checked <= peer->fail_timeout)

            {

                continue;

            }

            peer->current_weight += peer->effective_weight;

            total += peer->effective_weight;

            if (peer->effective_weight < peer->weight) {

                peer->effective_weight++;

            }

            if (best == NULL || peer->current_weight > best->current_weight) {

                best = peer;

            }

        }

        if (best) {

            best->current_weight -= total;

            i = best - &hp->rrp.peers->peer[0];

            hp->rrp.current = i;

            n = i / (8 * sizeof(uintptr_t));

            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

            hp->rrp.tried[n] |= m;

            if (now - best->checked > best->fail_timeout) {

                best->checked = now;

            }

            pc->sockaddr = best->sockaddr;

            pc->socklen = best->socklen;

            pc->name = &best->name;

            return NGX_OK;

        }

        hp->hash++;

        hp->tries++;

        if (hp->tries >= points->number) {

            return NGX_BUSY;

        }

    }

}

static void *

‌ngx_http_upstream_hash_create_conf(ngx_conf_t *cf)

{

    ngx_http_upstream_hash_srv_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->points = NULL;

    return conf;

}

static char *

‌ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_upstream_hash_srv_conf_t  *hcf = conf;

    ngx_str_t                         *value;

    ngx_http_upstream_srv_conf_t      *uscf;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &hcf->key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (uscf->peer.init_upstream) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "load balancing method redefined");

    }

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE

                  |NGX_HTTP_UPSTREAM_WEIGHT

                  |NGX_HTTP_UPSTREAM_MAX_FAILS

                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT

                  |NGX_HTTP_UPSTREAM_DOWN;

    if (cf->args->nelts == 2) {

        uscf->peer.init_upstream = ngx_http_upstream_init_hash;

    } else if (ngx_strcmp(value[2].data, "consistent") == 0) {

        uscf->peer.init_upstream = ngx_http_upstream_init_chash;

    } else {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid parameter \"%V\"", &value[2]);

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_upstream_ip_hash_module.c - nginx-1.7.9

Global variables defined

ngx_http_upstream_ip_hash_commands

ngx_http_upstream_ip_hash_module

ngx_http_upstream_ip_hash_module_ctx

ngx_http_upstream_ip_hash_pseudo_addr

Data types defined

ngx_http_upstream_ip_hash_peer_data_t

Functions defined

ngx_http_upstream_get_ip_hash_peer

ngx_http_upstream_init_ip_hash

ngx_http_upstream_init_ip_hash_peer

ngx_http_upstream_ip_hash

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    /* the round robin data must be first */

    ngx_http_upstream_rr_peer_data_t   rrp;

    ngx_uint_t                         hash;

    u_char                             addrlen;

    u_char                            *addr;

    u_char                             tries;

    ngx_event_get_peer_pt              get_rr_peer;

‌} ngx_http_upstream_ip_hash_peer_data_t;

static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,

    void *data);

static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {

    { ngx_string("ip_hash"),

      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,

      ngx_http_upstream_ip_hash,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_upstream_ip_hash_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* 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 */

};

‌ngx_module_t  ngx_http_upstream_ip_hash_module = {

    NGX_MODULE_V1,

    &ngx_http_upstream_ip_hash_module_ctx, /* module context */

    ngx_http_upstream_ip_hash_commands,    /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static u_char ngx_http_upstream_ip_hash_pseudo_addr[3];

static ngx_int_t

‌ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)

{

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {

        return NGX_ERROR;

    }

    us->peer.init = ngx_http_upstream_init_ip_hash_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    struct sockaddr_in                     *sin;

#if (NGX_HAVE_INET6)

    struct sockaddr_in6                    *sin6;

#endif

    ngx_http_upstream_ip_hash_peer_data_t  *iphp;

    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));

    if (iphp == NULL) {

        return NGX_ERROR;

    }

    r->upstream->peer.data = &iphp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {

        return NGX_ERROR;

    }

    r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;

    switch (r->connection->sockaddr->sa_family) {

    case AF_INET:

        sin = (struct sockaddr_in *) r->connection->sockaddr;

        iphp->addr = (u_char *) &sin->sin_addr.s_addr;

        iphp->addrlen = 3;

        break;

#if (NGX_HAVE_INET6)

    case AF_INET6:

        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;

        iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;

        iphp->addrlen = 16;

        break;

#endif

    default:

        iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;

        iphp->addrlen = 3;

    }

    iphp->hash = 89;

    iphp->tries = 0;

    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;

    time_t                        now;

    ngx_int_t                     w;

    uintptr_t                     m;

    ngx_uint_t                    i, n, p, hash;

    ngx_http_upstream_rr_peer_t  *peer;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get ip hash peer, try: %ui", pc->tries);

    /* TODO: cached */

    if (iphp->tries > 20 || iphp->rrp.peers->single) {

        return iphp->get_rr_peer(pc, &iphp->rrp);

    }

    now = ngx_time();

    pc->cached = 0;

    pc->connection = NULL;

    hash = iphp->hash;

    for ( ;; ) {

        for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {

            hash = (hash * 113 + iphp->addr[i]) % 6271;

        }

        if (!iphp->rrp.peers->weighted) {

            p = hash % iphp->rrp.peers->number;

        } else {

            w = hash % iphp->rrp.peers->total_weight;

            for (i = 0; i < iphp->rrp.peers->number; i++) {

                w -= iphp->rrp.peers->peer[i].weight;

                if (w < 0) {

                    break;

                }

            }

            p = i;

        }

        n = p / (8 * sizeof(uintptr_t));

        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

        if (iphp->rrp.tried[n] & m) {

            goto next;

        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get ip hash peer, hash: %ui %04XA", p, m);

        peer = &iphp->rrp.peers->peer[p];

        /* ngx_lock_mutex(iphp->rrp.peers->mutex); */

        if (peer->down) {

            goto next_try;

        }

        if (peer->max_fails

            && peer->fails >= peer->max_fails

            && now - peer->checked <= peer->fail_timeout)

        {

            goto next_try;

        }

        break;

    next_try:

        iphp->rrp.tried[n] |= m;

        /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */

        pc->tries--;

    next:

        if (++iphp->tries > 20) {

            return iphp->get_rr_peer(pc, &iphp->rrp);

        }

    }

    iphp->rrp.current = p;

    pc->sockaddr = peer->sockaddr;

    pc->socklen = peer->socklen;

    pc->name = &peer->name;

    if (now - peer->checked > peer->fail_timeout) {

        peer->checked = now;

    }

    /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */

    iphp->rrp.tried[n] |= m;

    iphp->hash = hash;

    return NGX_OK;

}

static char *

‌ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_upstream_srv_conf_t  *uscf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (uscf->peer.init_upstream) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "load balancing method redefined");

    }

    uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE

                  |NGX_HTTP_UPSTREAM_WEIGHT

                  |NGX_HTTP_UPSTREAM_MAX_FAILS

                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT

                  |NGX_HTTP_UPSTREAM_DOWN;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_upstream_keepalive_module.c - nginx-1.7.9

Global variables defined

ngx_http_upstream_keepalive_commands

ngx_http_upstream_keepalive_module

ngx_http_upstream_keepalive_module_ctx

Data types defined

ngx_http_upstream_keepalive_cache_t

ngx_http_upstream_keepalive_peer_data_t

ngx_http_upstream_keepalive_srv_conf_t

Functions defined

ngx_http_upstream_free_keepalive_peer

ngx_http_upstream_get_keepalive_peer

ngx_http_upstream_init_keepalive

ngx_http_upstream_init_keepalive_peer

ngx_http_upstream_keepalive

ngx_http_upstream_keepalive_close

ngx_http_upstream_keepalive_close_handler

ngx_http_upstream_keepalive_create_conf

ngx_http_upstream_keepalive_dummy_handler

ngx_http_upstream_keepalive_save_session

ngx_http_upstream_keepalive_set_session

Source code

/*

* Copyright (C) Maxim Dounin

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_uint_t                         max_cached;

    ngx_queue_t                        cache;

    ngx_queue_t                        free;

    ngx_http_upstream_init_pt          original_init_upstream;

    ngx_http_upstream_init_peer_pt     original_init_peer;

‌} ngx_http_upstream_keepalive_srv_conf_t;

typedef struct {

    ngx_http_upstream_keepalive_srv_conf_t  *conf;

    ngx_http_upstream_t               *upstream;

    void                              *data;

    ngx_event_get_peer_pt              original_get_peer;

    ngx_event_free_peer_pt             original_free_peer;

#if (NGX_HTTP_SSL)

    ngx_event_set_peer_session_pt      original_set_session;

    ngx_event_save_peer_session_pt     original_save_session;

#endif

‌} ngx_http_upstream_keepalive_peer_data_t;

typedef struct {

    ngx_http_upstream_keepalive_srv_conf_t  *conf;

    ngx_queue_t                        queue;

    ngx_connection_t                  *connection;

    socklen_t                          socklen;

    u_char                             sockaddr[NGX_SOCKADDRLEN];

‌} ngx_http_upstream_keepalive_cache_t;

static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,

    void *data);

static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,

    void *data, ngx_uint_t state);

static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);

static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);

static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);

#if (NGX_HTTP_SSL)

static ngx_int_t ngx_http_upstream_keepalive_set_session(

    ngx_peer_connection_t *pc, void *data);

static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,

    void *data);

#endif

static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);

static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_upstream_keepalive_commands[] = {

    { ngx_string("keepalive"),

      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_keepalive,

      NGX_HTTP_SRV_CONF_OFFSET,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_upstream_keepalive_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_http_upstream_keepalive_create_conf, /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_upstream_keepalive_module = {

    NGX_MODULE_V1,

    &ngx_http_upstream_keepalive_module_ctx, /* module context */

    ngx_http_upstream_keepalive_commands,    /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_upstream_init_keepalive(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_uint_t                               i;

    ngx_http_upstream_keepalive_srv_conf_t  *kcf;

    ngx_http_upstream_keepalive_cache_t     *cached;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,

                   "init keepalive");

    kcf = ngx_http_conf_upstream_srv_conf(us,

                                          ngx_http_upstream_keepalive_module);

    if (kcf->original_init_upstream(cf, us) != NGX_OK) {

        return NGX_ERROR;

    }

    kcf->original_init_peer = us->peer.init;

    us->peer.init = ngx_http_upstream_init_keepalive_peer;

    /* allocate cache items and add to free queue */

    cached = ngx_pcalloc(cf->pool,

                sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);

    if (cached == NULL) {

        return NGX_ERROR;

    }

    ngx_queue_init(&kcf->cache);

    ngx_queue_init(&kcf->free);

    for (i = 0; i < kcf->max_cached; i++) {

        ngx_queue_insert_head(&kcf->free, &cached[i].queue);

        cached[i].conf = kcf;

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_http_upstream_keepalive_peer_data_t  *kp;

    ngx_http_upstream_keepalive_srv_conf_t   *kcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "init keepalive peer");

    kcf = ngx_http_conf_upstream_srv_conf(us,

                                          ngx_http_upstream_keepalive_module);

    kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));

    if (kp == NULL) {

        return NGX_ERROR;

    }

    if (kcf->original_init_peer(r, us) != NGX_OK) {

        return NGX_ERROR;

    }

    kp->conf = kcf;

    kp->upstream = r->upstream;

    kp->data = r->upstream->peer.data;

    kp->original_get_peer = r->upstream->peer.get;

    kp->original_free_peer = r->upstream->peer.free;

    r->upstream->peer.data = kp;

    r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;

    r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;

#if (NGX_HTTP_SSL)

    kp->original_set_session = r->upstream->peer.set_session;

    kp->original_save_session = r->upstream->peer.save_session;

    r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;

    r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_keepalive_peer_data_t  *kp = data;

    ngx_http_upstream_keepalive_cache_t      *item;

    ngx_int_t          rc;

    ngx_queue_t       *q, *cache;

    ngx_connection_t  *c;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get keepalive peer");

    /* ask balancer */

    rc = kp->original_get_peer(pc, kp->data);

    if (rc != NGX_OK) {

        return rc;

    }

    /* search cache for suitable connection */

    cache = &kp->conf->cache;

    for (q = ngx_queue_head(cache);

         q != ngx_queue_sentinel(cache);

         q = ngx_queue_next(q))

    {

        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

        c = item->connection;

        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,

                         item->socklen, pc->socklen)

            == 0)

        {

            ngx_queue_remove(q);

            ngx_queue_insert_head(&kp->conf->free, q);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                           "get keepalive peer: using connection %p", c);

            c->idle = 0;

            c->sent = 0;

            c->log = pc->log;

            c->read->log = pc->log;

            c->write->log = pc->log;

            c->pool->log = pc->log;

            pc->connection = c;

            pc->cached = 1;

            return NGX_DONE;

        }

    }

    return NGX_OK;

}

static void

‌ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,

    ngx_uint_t state)

{

    ngx_http_upstream_keepalive_peer_data_t  *kp = data;

    ngx_http_upstream_keepalive_cache_t      *item;

    ngx_queue_t          *q;

    ngx_connection_t     *c;

    ngx_http_upstream_t  *u;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "free keepalive peer");

    /* cache valid connections */

    u = kp->upstream;

    c = pc->connection;

    if (state & NGX_PEER_FAILED

        || c == NULL

        || c->read->eof

        || c->read->error

        || c->read->timedout

        || c->write->error

        || c->write->timedout)

    {

        goto invalid;

    }

    if (!u->keepalive) {

        goto invalid;

    }

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

        goto invalid;

    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "free keepalive peer: saving connection %p", c);

    if (ngx_queue_empty(&kp->conf->free)) {

        q = ngx_queue_last(&kp->conf->cache);

        ngx_queue_remove(q);

        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

        ngx_http_upstream_keepalive_close(item->connection);

    } else {

        q = ngx_queue_head(&kp->conf->free);

        ngx_queue_remove(q);

        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

    }

    item->connection = c;

    ngx_queue_insert_head(&kp->conf->cache, q);

    pc->connection = NULL;

    if (c->read->timer_set) {

        ngx_del_timer(c->read);

    }

    if (c->write->timer_set) {

        ngx_del_timer(c->write);

    }

    c->write->handler = ngx_http_upstream_keepalive_dummy_handler;

    c->read->handler = ngx_http_upstream_keepalive_close_handler;

    c->data = item;

    c->idle = 1;

    c->log = ngx_cycle->log;

    c->read->log = ngx_cycle->log;

    c->write->log = ngx_cycle->log;

    c->pool->log = ngx_cycle->log;

    item->socklen = pc->socklen;

    ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);

    if (c->read->ready) {

        ngx_http_upstream_keepalive_close_handler(c->read);

    }

invalid:

    kp->original_free_peer(pc, kp->data, state);

}

static void

‌ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,

                   "keepalive dummy handler");

}

static void

‌ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)

{

    ngx_http_upstream_keepalive_srv_conf_t  *conf;

    ngx_http_upstream_keepalive_cache_t     *item;

    int                n;

    char               buf[1];

    ngx_connection_t  *c;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,

                   "keepalive close handler");

    c = ev->data;

    if (c->close) {

        goto close;

    }

    n = recv(c->fd, buf, 1, MSG_PEEK);

    if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {

        /* stale event */

        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {

            goto close;

        }

        return;

    }

close:

    item = c->data;

    conf = item->conf;

    ngx_http_upstream_keepalive_close(c);

    ngx_queue_remove(&item->queue);

    ngx_queue_insert_head(&conf->free, &item->queue);

}

static void

‌ngx_http_upstream_keepalive_close(ngx_connection_t *c)

{

#if (NGX_HTTP_SSL)

    if (c->ssl) {

        c->ssl->no_wait_shutdown = 1;

        c->ssl->no_send_shutdown = 1;

        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {

            c->ssl->handler = ngx_http_upstream_keepalive_close;

            return;

        }

    }

#endif

    ngx_destroy_pool(c->pool);

    ngx_close_connection(c);

}

#if (NGX_HTTP_SSL)

static ngx_int_t

‌ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_keepalive_peer_data_t  *kp = data;

    return kp->original_set_session(pc, kp->data);

}

static void

‌ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_keepalive_peer_data_t  *kp = data;

    kp->original_save_session(pc, kp->data);

    return;

}

#endif

static void *

‌ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)

{

    ngx_http_upstream_keepalive_srv_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool,

                       sizeof(ngx_http_upstream_keepalive_srv_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->original_init_upstream = NULL;

     *     conf->original_init_peer = NULL;

     */

    conf->max_cached = 1;

    return conf;

}

static char *

‌ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_upstream_srv_conf_t            *uscf;

    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;

    ngx_int_t    n;

    ngx_str_t   *value;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (kcf->original_init_upstream) {

        return "is duplicate";

    }

    kcf->original_init_upstream = uscf->peer.init_upstream

                                  ? uscf->peer.init_upstream

                                  : ngx_http_upstream_init_round_robin;

    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;

    /* read options */

    value = cf->args->elts;

    n = ngx_atoi(value[1].data, value[1].len);

    if (n == NGX_ERROR || n == 0) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "invalid value \"%V\" in \"%V\" directive",

                           &value[1], &cmd->name);

        return NGX_CONF_ERROR;

    }

    kcf->max_cached = n;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_upstream_least_conn_module.c - nginx-1.7.9

Global variables defined

ngx_http_upstream_least_conn_commands

ngx_http_upstream_least_conn_module

ngx_http_upstream_least_conn_module_ctx

Data types defined

ngx_http_upstream_lc_peer_data_t

ngx_http_upstream_least_conn_conf_t

Functions defined

ngx_http_upstream_free_least_conn_peer

ngx_http_upstream_get_least_conn_peer

ngx_http_upstream_init_least_conn

ngx_http_upstream_init_least_conn_peer

ngx_http_upstream_least_conn

ngx_http_upstream_least_conn_create_conf

Source code

/*

* Copyright (C) Maxim Dounin

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_uint_t                        *conns;

‌} ngx_http_upstream_least_conn_conf_t;

typedef struct {

    /* the round robin data must be first */

    ngx_http_upstream_rr_peer_data_t   rrp;

    ngx_uint_t                        *conns;

    ngx_event_get_peer_pt              get_rr_peer;

    ngx_event_free_peer_pt             free_rr_peer;

‌} ngx_http_upstream_lc_peer_data_t;

static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_least_conn_peer(

    ngx_peer_connection_t *pc, void *data);

static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,

    void *data, ngx_uint_t state);

static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf);

static char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

‌static ngx_command_t  ngx_http_upstream_least_conn_commands[] = {

    { ngx_string("least_conn"),

      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,

      ngx_http_upstream_least_conn,

      0,

      0,

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_upstream_least_conn_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    ngx_http_upstream_least_conn_create_conf, /* create server configuration */

    NULL,                                  /* merge server configuration */

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

‌ngx_module_t  ngx_http_upstream_least_conn_module = {

    NGX_MODULE_V1,

    &ngx_http_upstream_least_conn_module_ctx, /* module context */

    ngx_http_upstream_least_conn_commands, /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static ngx_int_t

‌ngx_http_upstream_init_least_conn(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_uint_t                            n;

    ngx_http_upstream_rr_peers_t         *peers;

    ngx_http_upstream_least_conn_conf_t  *lcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,

                   "init least conn");

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {

        return NGX_ERROR;

    }

    peers = us->peer.data;

    n = peers->number;

    if (peers->next) {

        n += peers->next->number;

    }

    lcf = ngx_http_conf_upstream_srv_conf(us,

                                          ngx_http_upstream_least_conn_module);

    lcf->conns = ngx_pcalloc(cf->pool, sizeof(ngx_uint_t) * n);

    if (lcf->conns == NULL) {

        return NGX_ERROR;

    }

    us->peer.init = ngx_http_upstream_init_least_conn_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

    ngx_http_upstream_lc_peer_data_t     *lcp;

    ngx_http_upstream_least_conn_conf_t  *lcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "init least conn peer");

    lcf = ngx_http_conf_upstream_srv_conf(us,

                                          ngx_http_upstream_least_conn_module);

    lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));

    if (lcp == NULL) {

        return NGX_ERROR;

    }

    lcp->conns = lcf->conns;

    r->upstream->peer.data = &lcp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {

        return NGX_ERROR;

    }

    r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;

    r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;

    lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_lc_peer_data_t  *lcp = data;

    time_t                         now;

    uintptr_t                      m;

    ngx_int_t                      rc, total;

    ngx_uint_t                     i, n, p, many;

    ngx_http_upstream_rr_peer_t   *peer, *best;

    ngx_http_upstream_rr_peers_t  *peers;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "get least conn peer, try: %ui", pc->tries);

    if (lcp->rrp.peers->single) {

        return lcp->get_rr_peer(pc, &lcp->rrp);

    }

    pc->cached = 0;

    pc->connection = NULL;

    now = ngx_time();

    peers = lcp->rrp.peers;

    best = NULL;

    total = 0;

#if (NGX_SUPPRESS_WARN)

    many = 0;

    p = 0;

#endif

    for (i = 0; i < peers->number; i++) {

        n = i / (8 * sizeof(uintptr_t));

        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

        if (lcp->rrp.tried[n] & m) {

            continue;

        }

        peer = &peers->peer[i];

        if (peer->down) {

            continue;

        }

        if (peer->max_fails

            && peer->fails >= peer->max_fails

            && now - peer->checked <= peer->fail_timeout)

        {

            continue;

        }

        /*

         * select peer with least number of connections; if there are

         * multiple peers with the same number of connections, select

         * based on round-robin

         */

        if (best == NULL

            || lcp->conns[i] * best->weight < lcp->conns[p] * peer->weight)

        {

            best = peer;

            many = 0;

            p = i;

        } else if (lcp->conns[i] * best->weight

                   == lcp->conns[p] * peer->weight)

        {

            many = 1;

        }

    }

    if (best == NULL) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get least conn peer, no peer found");

        goto failed;

    }

    if (many) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get least conn peer, many");

        for (i = p; i < peers->number; i++) {

            n = i / (8 * sizeof(uintptr_t));

            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

            if (lcp->rrp.tried[n] & m) {

                continue;

            }

            peer = &peers->peer[i];

            if (peer->down) {

                continue;

            }

            if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {

                continue;

            }

            if (peer->max_fails

                && peer->fails >= peer->max_fails

                && now - peer->checked <= peer->fail_timeout)

            {

                continue;

            }

            peer->current_weight += peer->effective_weight;

            total += peer->effective_weight;

            if (peer->effective_weight < peer->weight) {

                peer->effective_weight++;

            }

            if (peer->current_weight > best->current_weight) {

                best = peer;

                p = i;

            }

        }

    }

    best->current_weight -= total;

    if (now - best->checked > best->fail_timeout) {

        best->checked = now;

    }

    pc->sockaddr = best->sockaddr;

    pc->socklen = best->socklen;

    pc->name = &best->name;

    lcp->rrp.current = p;

    n = p / (8 * sizeof(uintptr_t));

    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

    lcp->rrp.tried[n] |= m;

    lcp->conns[p]++;

    return NGX_OK;

failed:

    if (peers->next) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                       "get least conn peer, backup servers");

        lcp->conns += peers->number;

        lcp->rrp.peers = peers->next;

        n = (lcp->rrp.peers->number + (8 * sizeof(uintptr_t) - 1))

                / (8 * sizeof(uintptr_t));

        for (i = 0; i < n; i++) {

             lcp->rrp.tried[i] = 0;

        }

        rc = ngx_http_upstream_get_least_conn_peer(pc, lcp);

        if (rc != NGX_BUSY) {

            return rc;

        }

    }

    /* all peers failed, mark them as live for quick recovery */

    for (i = 0; i < peers->number; i++) {

        peers->peer[i].fails = 0;

    }

    pc->name = peers->name;

    return NGX_BUSY;

}

static void

‌ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,

    void *data, ngx_uint_t state)

{

    ngx_http_upstream_lc_peer_data_t  *lcp = data;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "free least conn peer %ui %ui", pc->tries, state);

    if (lcp->rrp.peers->single) {

        lcp->free_rr_peer(pc, &lcp->rrp, state);

        return;

    }

    lcp->conns[lcp->rrp.current]--;

    lcp->free_rr_peer(pc, &lcp->rrp, state);

}

static void *

‌ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf)

{

    ngx_http_upstream_least_conn_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool,

                       sizeof(ngx_http_upstream_least_conn_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->conns = NULL;

     */

    return conf;

}

static char *

‌ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_upstream_srv_conf_t  *uscf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (uscf->peer.init_upstream) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "load balancing method redefined");

    }

    uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE

                  |NGX_HTTP_UPSTREAM_WEIGHT

                  |NGX_HTTP_UPSTREAM_MAX_FAILS

                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT

                  |NGX_HTTP_UPSTREAM_DOWN

                  |NGX_HTTP_UPSTREAM_BACKUP;

    return NGX_CONF_OK;

}

src/http/modules/ngx_http_uwsgi_module.c - nginx-1.7.9

Global variables defined

ngx_http_uwsgi_cache_headers

ngx_http_uwsgi_commands

ngx_http_uwsgi_hide_headers

ngx_http_uwsgi_modifier_bounds

ngx_http_uwsgi_module

ngx_http_uwsgi_module

ngx_http_uwsgi_module_ctx

ngx_http_uwsgi_next_upstream_masks

ngx_http_uwsgi_ssl_protocols

ngx_http_uwsgi_temp_path

Data types defined

ngx_http_uwsgi_loc_conf_t

ngx_http_uwsgi_main_conf_t

ngx_http_uwsgi_params_t

Functions defined

ngx_http_uwsgi_abort_request

ngx_http_uwsgi_cache

ngx_http_uwsgi_cache_key

ngx_http_uwsgi_create_key

ngx_http_uwsgi_create_loc_conf

ngx_http_uwsgi_create_main_conf

ngx_http_uwsgi_create_request

ngx_http_uwsgi_eval

ngx_http_uwsgi_finalize_request

ngx_http_uwsgi_handler

ngx_http_uwsgi_init_params

ngx_http_uwsgi_merge_loc_conf

ngx_http_uwsgi_pass

ngx_http_uwsgi_process_header

ngx_http_uwsgi_process_status_line

ngx_http_uwsgi_reinit_request

ngx_http_uwsgi_set_ssl

ngx_http_uwsgi_ssl_password_file

ngx_http_uwsgi_store

Source code

/*

* Copyright (C) Unbit S.a.s. 2009-2010

* Copyright (C) 2008 Manlio Perillo (manlio.perillo@gmail.com)

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_array_t                caches;  /* ngx_http_file_cache_t * */

‌} ngx_http_uwsgi_main_conf_t;

typedef struct {

    ngx_array_t               *flushes;

    ngx_array_t               *lengths;

    ngx_array_t               *values;

    ngx_uint_t                 number;

    ngx_hash_t                 hash;

‌} ngx_http_uwsgi_params_t;

typedef struct {

    ngx_http_upstream_conf_t   upstream;

    ngx_http_uwsgi_params_t    params;

#if (NGX_HTTP_CACHE)

    ngx_http_uwsgi_params_t    params_cache;

#endif

    ngx_array_t               *params_source;

    ngx_array_t               *uwsgi_lengths;

    ngx_array_t               *uwsgi_values;

#if (NGX_HTTP_CACHE)

    ngx_http_complex_value_t   cache_key;

#endif

    ngx_str_t                  uwsgi_string;

    ngx_uint_t                 modifier1;

    ngx_uint_t                 modifier2;

#if (NGX_HTTP_SSL)

    ngx_uint_t                 ssl;

    ngx_uint_t                 ssl_protocols;

    ngx_str_t                  ssl_ciphers;

    ngx_uint_t                 ssl_verify_depth;

    ngx_str_t                  ssl_trusted_certificate;

    ngx_str_t                  ssl_crl;

    ngx_str_t                  ssl_certificate;

    ngx_str_t                  ssl_certificate_key;

    ngx_array_t               *ssl_passwords;

#endif

‌} ngx_http_uwsgi_loc_conf_t;

static ngx_int_t ngx_http_uwsgi_eval(ngx_http_request_t *r,

    ngx_http_uwsgi_loc_conf_t *uwcf);

static ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r);

static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r);

static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r);

static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r);

static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r,

    ngx_int_t rc);

static void *ngx_http_uwsgi_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_uwsgi_init_params(ngx_conf_t *cf,

    ngx_http_uwsgi_loc_conf_t *conf, ngx_http_uwsgi_params_t *params,

    ngx_keyval_t *default_params);

static char *ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#if (NGX_HTTP_CACHE)

static ngx_int_t ngx_http_uwsgi_create_key(ngx_http_request_t *r);

static char *ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

#endif

#if (NGX_HTTP_SSL)

static char *ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf,

    ngx_command_t *cmd, void *conf);

static ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf,

    ngx_http_uwsgi_loc_conf_t *uwcf);

#endif

‌static ngx_conf_num_bounds_t  ngx_http_uwsgi_modifier_bounds = {

    ngx_conf_check_num_bounds, 0, 255

};

‌static ngx_conf_bitmask_t ngx_http_uwsgi_next_upstream_masks[] = {

    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },

    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },

    { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },

    { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },

    { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },

    { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },

    { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },

    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },

    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },

    { ngx_null_string, 0 }

};

#if (NGX_HTTP_SSL)

‌static ngx_conf_bitmask_t  ngx_http_uwsgi_ssl_protocols[] = {

    { ngx_string("SSLv2"), NGX_SSL_SSLv2 },

    { ngx_string("SSLv3"), NGX_SSL_SSLv3 },

    { ngx_string("TLSv1"), NGX_SSL_TLSv1 },

    { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },

    { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },

    { ngx_null_string, 0 }

};

#endif

‌ngx_module_t  ngx_http_uwsgi_module;

‌static ngx_command_t ngx_http_uwsgi_commands[] = {

    { ngx_string("uwsgi_pass"),

      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,

      ngx_http_uwsgi_pass,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("uwsgi_modifier1"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, modifier1),

      &ngx_http_uwsgi_modifier_bounds },

    { ngx_string("uwsgi_modifier2"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, modifier2),

      &ngx_http_uwsgi_modifier_bounds },

    { ngx_string("uwsgi_store"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_uwsgi_store,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("uwsgi_store_access"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,

      ngx_conf_set_access_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.store_access),

      NULL },

    { ngx_string("uwsgi_buffering"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffering),

      NULL },

    { ngx_string("uwsgi_ignore_client_abort"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ignore_client_abort),

      NULL },

    { ngx_string("uwsgi_bind"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_bind_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local),

      NULL },

    { ngx_string("uwsgi_connect_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.connect_timeout),

      NULL },

    { ngx_string("uwsgi_send_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.send_timeout),

      NULL },

    { ngx_string("uwsgi_buffer_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffer_size),

      NULL },

    { ngx_string("uwsgi_pass_request_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_headers),

      NULL },

    { ngx_string("uwsgi_pass_request_body"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_body),

      NULL },

    { ngx_string("uwsgi_intercept_errors"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.intercept_errors),

      NULL },

    { ngx_string("uwsgi_read_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.read_timeout),

      NULL },

    { ngx_string("uwsgi_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.bufs),

      NULL },

    { ngx_string("uwsgi_busy_buffers_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.busy_buffers_size_conf),

      NULL },

    { ngx_string("uwsgi_force_ranges"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.force_ranges),

      NULL },

    { ngx_string("uwsgi_limit_rate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.limit_rate),

      NULL },

#if (NGX_HTTP_CACHE)

    { ngx_string("uwsgi_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_uwsgi_cache,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("uwsgi_cache_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_uwsgi_cache_key,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("uwsgi_cache_path"),

      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,

      ngx_http_file_cache_set_slot,

      NGX_HTTP_MAIN_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_main_conf_t, caches),

      &ngx_http_uwsgi_module },

    { ngx_string("uwsgi_cache_bypass"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_bypass),

      NULL },

    { ngx_string("uwsgi_no_cache"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_set_predicate_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.no_cache),

      NULL },

    { ngx_string("uwsgi_cache_valid"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_file_cache_valid_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_valid),

      NULL },

    { ngx_string("uwsgi_cache_min_uses"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_min_uses),

      NULL },

    { ngx_string("uwsgi_cache_use_stale"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_use_stale),

      &ngx_http_uwsgi_next_upstream_masks },

    { ngx_string("uwsgi_cache_methods"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_methods),

      &ngx_http_upstream_cache_method_mask },

    { ngx_string("uwsgi_cache_lock"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock),

      NULL },

    { ngx_string("uwsgi_cache_lock_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_timeout),

      NULL },

    { ngx_string("uwsgi_cache_lock_age"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_age),

      NULL },

    { ngx_string("uwsgi_cache_revalidate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_revalidate),

      NULL },

#endif

    { ngx_string("uwsgi_temp_path"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,

      ngx_conf_set_path_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_path),

      NULL },

    { ngx_string("uwsgi_max_temp_file_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.max_temp_file_size_conf),

      NULL },

    { ngx_string("uwsgi_temp_file_write_size"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_size_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_file_write_size_conf),

      NULL },

    { ngx_string("uwsgi_next_upstream"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.next_upstream),

      &ngx_http_uwsgi_next_upstream_masks },

    { ngx_string("uwsgi_next_upstream_tries"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.next_upstream_tries),

      NULL },

    { ngx_string("uwsgi_next_upstream_timeout"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_msec_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.next_upstream_timeout),

      NULL },

    { ngx_string("uwsgi_param"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,

      ngx_http_upstream_param_set_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, params_source),

      NULL },

    { ngx_string("uwsgi_string"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, uwsgi_string),

      NULL },

    { ngx_string("uwsgi_pass_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_headers),

      NULL },

    { ngx_string("uwsgi_hide_header"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_array_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.hide_headers),

      NULL },

    { ngx_string("uwsgi_ignore_headers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ignore_headers),

      &ngx_http_upstream_ignore_headers_masks },

#if (NGX_HTTP_SSL)

    { ngx_string("uwsgi_ssl_session_reuse"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_session_reuse),

      NULL },

    { ngx_string("uwsgi_ssl_protocols"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_conf_set_bitmask_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_protocols),

      &ngx_http_uwsgi_ssl_protocols },

    { ngx_string("uwsgi_ssl_ciphers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_ciphers),

      NULL },

    { ngx_string("uwsgi_ssl_name"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_set_complex_value_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_name),

      NULL },

    { ngx_string("uwsgi_ssl_server_name"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_server_name),

      NULL },

    { ngx_string("uwsgi_ssl_verify"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_verify),

      NULL },

    { ngx_string("uwsgi_ssl_verify_depth"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_num_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_verify_depth),

      NULL },

    { ngx_string("uwsgi_ssl_trusted_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_trusted_certificate),

      NULL },

    { ngx_string("uwsgi_ssl_crl"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_crl),

      NULL },

    { ngx_string("uwsgi_ssl_certificate"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_certificate),

      NULL },

    { ngx_string("uwsgi_ssl_certificate_key"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_uwsgi_loc_conf_t, ssl_certificate_key),

      NULL },

    { ngx_string("uwsgi_ssl_password_file"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_uwsgi_ssl_password_file,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

#endif

      ngx_null_command

};

‌static ngx_http_module_t ngx_http_uwsgi_module_ctx = {

    NULL,                                  /* preconfiguration */

    NULL,                                  /* postconfiguration */

    ngx_http_uwsgi_create_main_conf,       /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_uwsgi_create_loc_conf,        /* create location configuration */

    ngx_http_uwsgi_merge_loc_conf          /* merge location configuration */

};

‌ngx_module_t ngx_http_uwsgi_module = {

    NGX_MODULE_V1,

    &ngx_http_uwsgi_module_ctx,            /* module context */

    ngx_http_uwsgi_commands,               /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_str_t ngx_http_uwsgi_hide_headers[] = {

    ngx_string("X-Accel-Expires"),

    ngx_string("X-Accel-Redirect"),

    ngx_string("X-Accel-Limit-Rate"),

    ngx_string("X-Accel-Buffering"),

    ngx_string("X-Accel-Charset"),

    ngx_null_string

};

#if (NGX_HTTP_CACHE)

‌static ngx_keyval_t  ngx_http_uwsgi_cache_headers[] = {

    { ngx_string("HTTP_IF_MODIFIED_SINCE"),

      ngx_string("$upstream_cache_last_modified") },

    { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },

    { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },

    { ngx_string("HTTP_IF_MATCH"), ngx_string("") },

    { ngx_string("HTTP_RANGE"), ngx_string("") },

    { ngx_string("HTTP_IF_RANGE"), ngx_string("") },

    { ngx_null_string, ngx_null_string }

};

#endif

‌static ngx_path_init_t ngx_http_uwsgi_temp_path = {

    ngx_string(NGX_HTTP_UWSGI_TEMP_PATH), { 1, 2, 0 }

};

static ngx_int_t

‌ngx_http_uwsgi_handler(ngx_http_request_t *r)

{

    ngx_int_t                    rc;

    ngx_http_status_t           *status;

    ngx_http_upstream_t         *u;

    ngx_http_uwsgi_loc_conf_t   *uwcf;

#if (NGX_HTTP_CACHE)

    ngx_http_uwsgi_main_conf_t  *uwmcf;

#endif

    if (ngx_http_upstream_create(r) != NGX_OK) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));

    if (status == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    ngx_http_set_ctx(r, status, ngx_http_uwsgi_module);

    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);

    u = r->upstream;

    if (uwcf->uwsgi_lengths == NULL) {

#if (NGX_HTTP_SSL)

        u->ssl = (uwcf->upstream.ssl != NULL);

        if (u->ssl) {

            ngx_str_set(&u->schema, "suwsgi://");

        } else {

            ngx_str_set(&u->schema, "uwsgi://");

        }

#else

        ngx_str_set(&u->schema, "uwsgi://");

#endif

    } else {

        if (ngx_http_uwsgi_eval(r, uwcf) != NGX_OK) {

            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        }

    }

    u->output.tag = (ngx_buf_tag_t) &ngx_http_uwsgi_module;

    u->conf = &uwcf->upstream;

#if (NGX_HTTP_CACHE)

    uwmcf = ngx_http_get_module_main_conf(r, ngx_http_uwsgi_module);

    u->caches = &uwmcf->caches;

    u->create_key = ngx_http_uwsgi_create_key;

#endif

    u->create_request = ngx_http_uwsgi_create_request;

    u->reinit_request = ngx_http_uwsgi_reinit_request;

    u->process_header = ngx_http_uwsgi_process_status_line;

    u->abort_request = ngx_http_uwsgi_abort_request;

    u->finalize_request = ngx_http_uwsgi_finalize_request;

    r->state = 0;

    u->buffering = uwcf->upstream.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));

    if (u->pipe == NULL) {

        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    }

    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;

    u->pipe->input_ctx = r;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

        return rc;

    }

    return NGX_DONE;

}

static ngx_int_t

‌ngx_http_uwsgi_eval(ngx_http_request_t *r, ngx_http_uwsgi_loc_conf_t * uwcf)

{

    size_t                add;

    ngx_url_t             url;

    ngx_http_upstream_t  *u;

    ngx_memzero(&url, sizeof(ngx_url_t));

    if (ngx_http_script_run(r, &url.url, uwcf->uwsgi_lengths->elts, 0,

                            uwcf->uwsgi_values->elts)

        == NULL)

    {

        return NGX_ERROR;

    }

    if (url.url.len > 8

        && ngx_strncasecmp(url.url.data, (u_char *) "uwsgi://", 8) == 0)

    {

        add = 8;

    } else if (url.url.len > 9

               && ngx_strncasecmp(url.url.data, (u_char *) "suwsgi://", 9) == 0)

    {

#if (NGX_HTTP_SSL)

        add = 9;

        r->upstream->ssl = 1;

#else

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "suwsgi protocol requires SSL support");

        return NGX_ERROR;

#endif

    } else {

        add = 0;

    }

    u = r->upstream;

    if (add) {

        u->schema.len = add;

        u->schema.data = url.url.data;

        url.url.data += add;

        url.url.len -= add;

    } else {

        ngx_str_set(&u->schema, "uwsgi://");

    }

    url.no_resolve = 1;

    if (ngx_parse_url(r->pool, &url) != NGX_OK) {

        if (url.err) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "%s in upstream \"%V\"", url.err, &url.url);

        }

        return NGX_ERROR;

    }

    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));

    if (u->resolved == NULL) {

        return NGX_ERROR;

    }

    if (url.addrs && url.addrs[0].sockaddr) {

        u->resolved->sockaddr = url.addrs[0].sockaddr;

        u->resolved->socklen = url.addrs[0].socklen;

        u->resolved->naddrs = 1;

        u->resolved->host = url.addrs[0].name;

    } else {

        u->resolved->host = url.host;

        u->resolved->port = url.port;

        u->resolved->no_port = url.no_port;

    }

    return NGX_OK;

}

#if (NGX_HTTP_CACHE)

static ngx_int_t

‌ngx_http_uwsgi_create_key(ngx_http_request_t *r)

{

    ngx_str_t                  *key;

    ngx_http_uwsgi_loc_conf_t  *uwcf;

    key = ngx_array_push(&r->cache->keys);

    if (key == NULL) {

        return NGX_ERROR;

    }

    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);

    if (ngx_http_complex_value(r, &uwcf->cache_key, key) != NGX_OK) {

        return NGX_ERROR;

    }

    return NGX_OK;

}

#endif

static ngx_int_t

‌ngx_http_uwsgi_create_request(ngx_http_request_t *r)

{

    u_char                        ch, *lowcase_key;

    size_t                        key_len, val_len, len, allocated;

    ngx_uint_t                    i, n, hash, skip_empty, header_params;

    ngx_buf_t                    *b;

    ngx_chain_t                  *cl, *body;

    ngx_list_part_t              *part;

    ngx_table_elt_t              *header, **ignored;

    ngx_http_uwsgi_params_t      *params;

    ngx_http_script_code_pt       code;

    ngx_http_script_engine_t      e, le;

    ngx_http_uwsgi_loc_conf_t    *uwcf;

    ngx_http_script_len_code_pt   lcode;

    len = 0;

    header_params = 0;

    ignored = NULL;

    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);

#if (NGX_HTTP_CACHE)

    params = r->upstream->cacheable ? &uwcf->params_cache : &uwcf->params;

#else

    params = &uwcf->params;

#endif

    if (params->lengths) {

        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);

        le.flushed = 1;

        le.ip = params->lengths->elts;

        le.request = r;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            key_len = lcode(&le);

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode (&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                continue;

            }

            len += 2 + key_len + 2 + val_len;

        }

    }

    if (uwcf->upstream.pass_request_headers) {

        allocated = 0;

        lowcase_key = NULL;

        if (params->number) {

            n = 0;

            part = &r->headers_in.headers.part;

            while (part) {

                n += part->nelts;

                part = part->next;

            }

            ignored = ngx_palloc(r->pool, n * sizeof(void *));

            if (ignored == NULL) {

                return NGX_ERROR;

            }

        }

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */ ; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            if (params->number) {

                if (allocated < header[i].key.len) {

                    allocated = header[i].key.len + 16;

                    lowcase_key = ngx_pnalloc(r->pool, allocated);

                    if (lowcase_key == NULL) {

                        return NGX_ERROR;

                    }

                }

                hash = 0;

                for (n = 0; n < header[i].key.len; n++) {

                    ch = header[i].key.data[n];

                    if (ch >= 'A' && ch <= 'Z') {

                        ch |= 0x20;

                    } else if (ch == '-') {

                        ch = '_';

                    }

                    hash = ngx_hash(hash, ch);

                    lowcase_key[n] = ch;

                }

                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {

                    ignored[header_params++] = &header[i];

                    continue;

                }

            }

            len += 2 + sizeof("HTTP_") - 1 + header[i].key.len

                 + 2 + header[i].value.len;

        }

    }

    len += uwcf->uwsgi_string.len;

#if 0

    /* allow custom uwsgi packet */

    if (len > 0 && len < 2) {

        ngx_log_error (NGX_LOG_ALERT, r->connection->log, 0,

                       "uwsgi request is too little: %uz", len);

        return NGX_ERROR;

    }

#endif

    b = ngx_create_temp_buf(r->pool, len + 4);

    if (b == NULL) {

        return NGX_ERROR;

    }

    cl = ngx_alloc_chain_link(r->pool);

    if (cl == NULL) {

        return NGX_ERROR;

    }

    cl->buf = b;

    *b->last++ = (u_char) uwcf->modifier1;

    *b->last++ = (u_char) (len & 0xff);

    *b->last++ = (u_char) ((len >> 8) & 0xff);

    *b->last++ = (u_char) uwcf->modifier2;

    if (params->lengths) {

        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

        e.ip = params->values->elts;

        e.pos = b->last;

        e.request = r;

        e.flushed = 1;

        le.ip = params->lengths->elts;

        while (*(uintptr_t *) le.ip) {

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            key_len = (u_char) lcode (&le);

            lcode = *(ngx_http_script_len_code_pt *) le.ip;

            skip_empty = lcode(&le);

            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {

                lcode = *(ngx_http_script_len_code_pt *) le.ip;

            }

            le.ip += sizeof(uintptr_t);

            if (skip_empty && val_len == 0) {

                e.skip = 1;

                while (*(uintptr_t *) e.ip) {

                    code = *(ngx_http_script_code_pt *) e.ip;

                    code((ngx_http_script_engine_t *) &e);

                }

                e.ip += sizeof(uintptr_t);

                e.skip = 0;

                continue;

            }

            *e.pos++ = (u_char) (key_len & 0xff);

            *e.pos++ = (u_char) ((key_len >> 8) & 0xff);

            code = *(ngx_http_script_code_pt *) e.ip;

            code((ngx_http_script_engine_t *) & e);

            *e.pos++ = (u_char) (val_len & 0xff);

            *e.pos++ = (u_char) ((val_len >> 8) & 0xff);

            while (*(uintptr_t *) e.ip) {

                code = *(ngx_http_script_code_pt *) e.ip;

                code((ngx_http_script_engine_t *) & e);

            }

            e.ip += sizeof(uintptr_t);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "uwsgi param: \"%*s: %*s\"",

                           key_len, e.pos - (key_len + 2 + val_len),

                           val_len, e.pos - val_len);

        }

        b->last = e.pos;

    }

    if (uwcf->upstream.pass_request_headers) {

        part = &r->headers_in.headers.part;

        header = part->elts;

        for (i = 0; /* void */ ; i++) {

            if (i >= part->nelts) {

                if (part->next == NULL) {

                    break;

                }

                part = part->next;

                header = part->elts;

                i = 0;

            }

            for (n = 0; n < header_params; n++) {

                if (&header[i] == ignored[n]) {

                    goto next;

                }

            }

            key_len = sizeof("HTTP_") - 1 + header[i].key.len;

            *b->last++ = (u_char) (key_len & 0xff);

            *b->last++ = (u_char) ((key_len >> 8) & 0xff);

            b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);

            for (n = 0; n < header[i].key.len; n++) {

                ch = header[i].key.data[n];

                if (ch >= 'a' && ch <= 'z') {

                    ch &= ~0x20;

                } else if (ch == '-') {

                    ch = '_';

                }

                *b->last++ = ch;

            }

            val_len = header[i].value.len;

            *b->last++ = (u_char) (val_len & 0xff);

            *b->last++ = (u_char) ((val_len >> 8) & 0xff);

            b->last = ngx_copy(b->last, header[i].value.data, val_len);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "uwsgi param: \"%*s: %*s\"",

                           key_len, b->last - (key_len + 2 + val_len),

                           val_len, b->last - val_len);

        next:

            continue;

        }

    }

    b->last = ngx_copy(b->last, uwcf->uwsgi_string.data,

                       uwcf->uwsgi_string.len);

    if (uwcf->upstream.pass_request_body) {

        body = r->upstream->request_bufs;

        r->upstream->request_bufs = cl;

        while (body) {

            b = ngx_alloc_buf(r->pool);

            if (b == NULL) {

                return NGX_ERROR;

            }

            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

            cl->next = ngx_alloc_chain_link(r->pool);

            if (cl->next == NULL) {

                return NGX_ERROR;

            }

            cl = cl->next;

            cl->buf = b;

            body = body->next;

        }

    } else {

        r->upstream->request_bufs = cl;

    }

    cl->next = NULL;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_uwsgi_reinit_request(ngx_http_request_t *r)

{

    ngx_http_status_t  *status;

    status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);

    if (status == NULL) {

        return NGX_OK;

    }

    status->code = 0;

    status->count = 0;

    status->start = NULL;

    status->end = NULL;

    r->upstream->process_header = ngx_http_uwsgi_process_status_line;

    r->state = 0;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_uwsgi_process_status_line(ngx_http_request_t *r)

{

    size_t                 len;

    ngx_int_t              rc;

    ngx_http_status_t     *status;

    ngx_http_upstream_t   *u;

    status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);

    if (status == NULL) {

        return NGX_ERROR;

    }

    u = r->upstream;

    rc = ngx_http_parse_status_line(r, &u->buffer, status);

    if (rc == NGX_AGAIN) {

        return rc;

    }

    if (rc == NGX_ERROR) {

        u->process_header = ngx_http_uwsgi_process_header;

        return ngx_http_uwsgi_process_header(r);

    }

    if (u->state && u->state->status == 0) {

        u->state->status = status->code;

    }

    u->headers_in.status_n = status->code;

    len = status->end - status->start;

    u->headers_in.status_line.len = len;

    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);

    if (u->headers_in.status_line.data == NULL) {

        return NGX_ERROR;

    }

    ngx_memcpy(u->headers_in.status_line.data, status->start, len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "http uwsgi status %ui \"%V\"",

                   u->headers_in.status_n, &u->headers_in.status_line);

    u->process_header = ngx_http_uwsgi_process_header;

    return ngx_http_uwsgi_process_header(r);

}

static ngx_int_t

‌ngx_http_uwsgi_process_header(ngx_http_request_t *r)

{

    ngx_str_t                      *status_line;

    ngx_int_t                       rc, status;

    ngx_table_elt_t                *h;

    ngx_http_upstream_t            *u;

    ngx_http_upstream_header_t     *hh;

    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    for ( ;; ) {

        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);

        if (rc == NGX_OK) {

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->upstream->headers_in.headers);

            if (h == NULL) {

                return NGX_ERROR;

            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;

            h->value.len = r->header_end - r->header_start;

            h->key.data = ngx_pnalloc(r->pool,

                                      h->key.len + 1 + h->value.len + 1

                                      + h->key.len);

            if (h->key.data == NULL) {

                return NGX_ERROR;

            }

            h->value.data = h->key.data + h->key.len + 1;

            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;

            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);

            h->key.data[h->key.len] = '\0';

            ngx_memcpy(h->value.data, r->header_start, h->value.len);

            h->value.data[h->value.len] = '\0';

            if (h->key.len == r->lowcase_index) {

                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {

                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);

            }

            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,

                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {

                return NGX_ERROR;

            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http uwsgi header: \"%V: %V\"", &h->key, &h->value);

            continue;

        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "http uwsgi header done");

            u = r->upstream;

            if (u->headers_in.status_n) {

                goto done;

            }

            if (u->headers_in.status) {

                status_line = &u->headers_in.status->value;

                status = ngx_atoi(status_line->data, 3);

                if (status == NGX_ERROR) {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                  "upstream sent invalid status \"%V\"",

                                  status_line);

                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;

                }

                u->headers_in.status_n = status;

                u->headers_in.status_line = *status_line;

            } else if (u->headers_in.location) {

                u->headers_in.status_n = 302;

                ngx_str_set(&u->headers_in.status_line,

                            "302 Moved Temporarily");

            } else {

                u->headers_in.status_n = 200;

                ngx_str_set(&u->headers_in.status_line, "200 OK");

            }

            if (u->state && u->state->status == 0) {

                u->state->status = u->headers_in.status_n;

            }

        done:

            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS

                && r->headers_in.upgrade)

            {

                u->upgrade = 1;

            }

            return NGX_OK;

        }

        if (rc == NGX_AGAIN) {

            return NGX_AGAIN;

        }

        /* there was error while a header line parsing */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "upstream sent invalid header");

        return NGX_HTTP_UPSTREAM_INVALID_HEADER;

    }

}

static void

‌ngx_http_uwsgi_abort_request(ngx_http_request_t *r)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "abort http uwsgi request");

    return;

}

static void

‌ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "finalize http uwsgi request");

    return;

}

static void *

‌ngx_http_uwsgi_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_uwsgi_main_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

#if (NGX_HTTP_CACHE)

    if (ngx_array_init(&conf->caches, cf->pool, 4,

                       sizeof(ngx_http_file_cache_t *))

        != NGX_OK)

    {

        return NULL;

    }

#endif

    return conf;

}

static void *

‌ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)

{

    ngx_http_uwsgi_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->modifier1 = NGX_CONF_UNSET_UINT;

    conf->modifier2 = NGX_CONF_UNSET_UINT;

    conf->upstream.store = NGX_CONF_UNSET;

    conf->upstream.store_access = NGX_CONF_UNSET_UINT;

    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

    conf->upstream.buffering = NGX_CONF_UNSET;

    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;

    conf->upstream.force_ranges = NGX_CONF_UNSET;

    conf->upstream.local = NGX_CONF_UNSET_PTR;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;

    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;

    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.pass_request_headers = NGX_CONF_UNSET;

    conf->upstream.pass_request_body = NGX_CONF_UNSET;

#if (NGX_HTTP_CACHE)

    conf->upstream.cache = NGX_CONF_UNSET;

    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;

    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;

    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;

    conf->upstream.cache_lock = NGX_CONF_UNSET;

    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;

    conf->upstream.cache_revalidate = NGX_CONF_UNSET;

#endif

    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.intercept_errors = NGX_CONF_UNSET;

#if (NGX_HTTP_SSL)

    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;

    conf->upstream.ssl_server_name = NGX_CONF_UNSET;

    conf->upstream.ssl_verify = NGX_CONF_UNSET;

    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;

    conf->ssl_passwords = NGX_CONF_UNSET_PTR;

#endif

    /* "uwsgi_cyclic_temp_file" is disabled */

    conf->upstream.cyclic_temp_file = 0;

    conf->upstream.change_buffering = 1;

    ngx_str_set(&conf->upstream.module, "uwsgi");

    return conf;

}

static char *

‌ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_uwsgi_loc_conf_t *prev = parent;

    ngx_http_uwsgi_loc_conf_t *conf = child;

    size_t                        size;

    ngx_int_t                     rc;

    ngx_hash_init_t               hash;

    ngx_http_core_loc_conf_t     *clcf;

#if (NGX_HTTP_CACHE)

    if (conf->upstream.store > 0) {

        conf->upstream.cache = 0;

    }

    if (conf->upstream.cache > 0) {

        conf->upstream.store = 0;

    }

#endif

    if (conf->upstream.store == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);

        conf->upstream.store_lengths = prev->upstream.store_lengths;

        conf->upstream.store_values = prev->upstream.store_values;

    }

    ngx_conf_merge_uint_value(conf->upstream.store_access,

                              prev->upstream.store_access, 0600);

    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,

                              prev->upstream.next_upstream_tries, 0);

    ngx_conf_merge_value(conf->upstream.buffering,

                              prev->upstream.buffering, 1);

    ngx_conf_merge_value(conf->upstream.ignore_client_abort,

                              prev->upstream.ignore_client_abort, 0);

    ngx_conf_merge_value(conf->upstream.force_ranges,

                              prev->upstream.force_ranges, 0);

    ngx_conf_merge_ptr_value(conf->upstream.local,

                              prev->upstream.local, NULL);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,

                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,

                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,

                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,

                              prev->upstream.next_upstream_timeout, 0);

    ngx_conf_merge_size_value(conf->upstream.send_lowat,

                              prev->upstream.send_lowat, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,

                              prev->upstream.buffer_size,

                              (size_t) ngx_pagesize);

    ngx_conf_merge_size_value(conf->upstream.limit_rate,

                              prev->upstream.limit_rate, 0);

    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,

                              8, ngx_pagesize);

    if (conf->upstream.bufs.num < 2) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "there must be at least 2 \"uwsgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    size = conf->upstream.buffer_size;

    if (size < conf->upstream.bufs.size) {

        size = conf->upstream.bufs.size;

    }

    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,

                              prev->upstream.busy_buffers_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.busy_buffers_size = 2 * size;

    } else {

        conf->upstream.busy_buffers_size =

            conf->upstream.busy_buffers_size_conf;

    }

    if (conf->upstream.busy_buffers_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"uwsgi_busy_buffers_size\" must be equal to or greater "

            "than the maximum of the value of \"uwsgi_buffer_size\" and "

            "one of the \"uwsgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    if (conf->upstream.busy_buffers_size

        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"uwsgi_busy_buffers_size\" must be less than "

            "the size of all \"uwsgi_buffers\" minus one buffer");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,

                              prev->upstream.temp_file_write_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.temp_file_write_size = 2 * size;

    } else {

        conf->upstream.temp_file_write_size =

            conf->upstream.temp_file_write_size_conf;

    }

    if (conf->upstream.temp_file_write_size < size) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"uwsgi_temp_file_write_size\" must be equal to or greater than "

            "the maximum of the value of \"uwsgi_buffer_size\" and "

            "one of the \"uwsgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,

                              prev->upstream.max_temp_file_size_conf,

                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {

        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;

    } else {

        conf->upstream.max_temp_file_size =

            conf->upstream.max_temp_file_size_conf;

    }

    if (conf->upstream.max_temp_file_size != 0

        && conf->upstream.max_temp_file_size < size)

    {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

            "\"uwsgi_max_temp_file_size\" must be equal to zero to disable "

            "temporary files usage or must be equal to or greater than "

            "the maximum of the value of \"uwsgi_buffer_size\" and "

            "one of the \"uwsgi_buffers\"");

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,

                                 prev->upstream.ignore_headers,

                                 NGX_CONF_BITMASK_SET);

    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,

                                 prev->upstream.next_upstream,

                                 (NGX_CONF_BITMASK_SET

                                  |NGX_HTTP_UPSTREAM_FT_ERROR

                                  |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET

                                       |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,

                                  prev->upstream.temp_path,

                                  &ngx_http_uwsgi_temp_path)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache == NGX_CONF_UNSET) {

        ngx_conf_merge_value(conf->upstream.cache,

                              prev->upstream.cache, 0);

        conf->upstream.cache_zone = prev->upstream.cache_zone;

        conf->upstream.cache_value = prev->upstream.cache_value;

    }

    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {

        ngx_shm_zone_t  *shm_zone;

        shm_zone = conf->upstream.cache_zone;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "\"uwsgi_cache\" zone \"%V\" is unknown",

                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,

                              prev->upstream.cache_min_uses, 1);

    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,

                              prev->upstream.cache_use_stale,

                              (NGX_CONF_BITMASK_SET

                               |NGX_HTTP_UPSTREAM_FT_OFF));

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {

        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET

                                         |NGX_HTTP_UPSTREAM_FT_OFF;

    }

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {

        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;

    }

    if (conf->upstream.cache_methods == 0) {

        conf->upstream.cache_methods = prev->upstream.cache_methods;

    }

    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;

    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,

                             prev->upstream.cache_bypass, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.no_cache,

                             prev->upstream.no_cache, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,

                             prev->upstream.cache_valid, NULL);

    if (conf->cache_key.value.data == NULL) {

        conf->cache_key = prev->cache_key;

    }

    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {

        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,

                           "no \"uwsgi_cache_key\" for \"uwsgi_cache\"");

    }

    ngx_conf_merge_value(conf->upstream.cache_lock,

                              prev->upstream.cache_lock, 0);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,

                              prev->upstream.cache_lock_timeout, 5000);

    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,

                              prev->upstream.cache_lock_age, 5000);

    ngx_conf_merge_value(conf->upstream.cache_revalidate,

                              prev->upstream.cache_revalidate, 0);

#endif

    ngx_conf_merge_value(conf->upstream.pass_request_headers,

                         prev->upstream.pass_request_headers, 1);

    ngx_conf_merge_value(conf->upstream.pass_request_body,

                         prev->upstream.pass_request_body, 1);

    ngx_conf_merge_value(conf->upstream.intercept_errors,

                         prev->upstream.intercept_errors, 0);

#if (NGX_HTTP_SSL)

    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,

                              prev->upstream.ssl_session_reuse, 1);

    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,

                                 (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3

                                  |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1

                                  |NGX_SSL_TLSv1_2));

    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,

                             "DEFAULT");

    if (conf->upstream.ssl_name == NULL) {

        conf->upstream.ssl_name = prev->upstream.ssl_name;

    }

    ngx_conf_merge_value(conf->upstream.ssl_server_name,

                              prev->upstream.ssl_server_name, 0);

    ngx_conf_merge_value(conf->upstream.ssl_verify,

                              prev->upstream.ssl_verify, 0);

    ngx_conf_merge_uint_value(conf->ssl_verify_depth,

                              prev->ssl_verify_depth, 1);

    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,

                              prev->ssl_trusted_certificate, "");

    ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");

    ngx_conf_merge_str_value(conf->ssl_certificate,

                              prev->ssl_certificate, "");

    ngx_conf_merge_str_value(conf->ssl_certificate_key,

                              prev->ssl_certificate_key, "");

    ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);

    if (conf->ssl && ngx_http_uwsgi_set_ssl(cf, conf) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#endif

    ngx_conf_merge_str_value(conf->uwsgi_string, prev->uwsgi_string, "");

    hash.max_size = 512;

    hash.bucket_size = ngx_align(64, ngx_cacheline_size);

    hash.name = "uwsgi_hide_headers_hash";

    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,

            &prev->upstream, ngx_http_uwsgi_hide_headers, &hash)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    if (clcf->noname

        && conf->upstream.upstream == NULL && conf->uwsgi_lengths == NULL)

    {

        conf->upstream.upstream = prev->upstream.upstream;

        conf->uwsgi_lengths = prev->uwsgi_lengths;

        conf->uwsgi_values = prev->uwsgi_values;

#if (NGX_HTTP_SSL)

        conf->upstream.ssl = prev->upstream.ssl;

#endif

    }

    if (clcf->lmt_excpt && clcf->handler == NULL

        && (conf->upstream.upstream || conf->uwsgi_lengths))

    {

        clcf->handler = ngx_http_uwsgi_handler;

    }

    ngx_conf_merge_uint_value(conf->modifier1, prev->modifier1, 0);

    ngx_conf_merge_uint_value(conf->modifier2, prev->modifier2, 0);

    if (conf->params_source == NULL) {

        conf->params = prev->params;

#if (NGX_HTTP_CACHE)

        conf->params_cache = prev->params_cache;

#endif

        conf->params_source = prev->params_source;

    }

    rc = ngx_http_uwsgi_init_params(cf, conf, &conf->params, NULL);

    if (rc != NGX_OK) {

        return NGX_CONF_ERROR;

    }

#if (NGX_HTTP_CACHE)

    if (conf->upstream.cache) {

        rc = ngx_http_uwsgi_init_params(cf, conf, &conf->params_cache,

                                        ngx_http_uwsgi_cache_headers);

        if (rc != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

#endif

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_uwsgi_init_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf,

    ngx_http_uwsgi_params_t *params, ngx_keyval_t *default_params)

{

    u_char                       *p;

    size_t                        size;

    uintptr_t                    *code;

    ngx_uint_t                    i, nsrc;

    ngx_array_t                   headers_names, params_merged;

    ngx_keyval_t                 *h;

    ngx_hash_key_t               *hk;

    ngx_hash_init_t               hash;

    ngx_http_upstream_param_t    *src, *s;

    ngx_http_script_compile_t     sc;

    ngx_http_script_copy_code_t  *copy;

    if (params->hash.buckets) {

        return NGX_OK;

    }

    if (conf->params_source == NULL && default_params == NULL) {

        params->hash.buckets = (void *) 1;

        return NGX_OK;

    }

    params->lengths = ngx_array_create(cf->pool, 64, 1);

    if (params->lengths == NULL) {

        return NGX_ERROR;

    }

    params->values = ngx_array_create(cf->pool, 512, 1);

    if (params->values == NULL) {

        return NGX_ERROR;

    }

    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    if (conf->params_source) {

        src = conf->params_source->elts;

        nsrc = conf->params_source->nelts;

    } else {

        src = NULL;

        nsrc = 0;

    }

    if (default_params) {

        if (ngx_array_init(&params_merged, cf->temp_pool, 4,

                           sizeof(ngx_http_upstream_param_t))

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        for (i = 0; i < nsrc; i++) {

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = src[i];

        }

        h = default_params;

        while (h->key.len) {

            src = params_merged.elts;

            nsrc = params_merged.nelts;

            for (i = 0; i < nsrc; i++) {

                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {

                    goto next;

                }

            }

            s = ngx_array_push(&params_merged);

            if (s == NULL) {

                return NGX_ERROR;

            }

            s->key = h->key;

            s->value = h->value;

            s->skip_empty = 1;

        next:

            h++;

        }

        src = params_merged.elts;

        nsrc = params_merged.nelts;

    }

    for (i = 0; i < nsrc; i++) {

        if (src[i].key.len > sizeof("HTTP_") - 1

            && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)

        {

            hk = ngx_array_push(&headers_names);

            if (hk == NULL) {

                return NGX_ERROR;

            }

            hk->key.len = src[i].key.len - 5;

            hk->key.data = src[i].key.data + 5;

            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);

            hk->value = (void *) 1;

            if (src[i].value.len == 0) {

                continue;

            }

        }

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].key.len;

        copy = ngx_array_push_n(params->lengths,

                                sizeof(ngx_http_script_copy_code_t));

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;

        copy->len = src[i].skip_empty;

        size = (sizeof(ngx_http_script_copy_code_t)

                + src[i].key.len + sizeof(uintptr_t) - 1)

               & ~(sizeof(uintptr_t) - 1);

        copy = ngx_array_push_n(params->values, size);

        if (copy == NULL) {

            return NGX_ERROR;

        }

        copy->code = ngx_http_script_copy_code;

        copy->len = src[i].key.len;

        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

        ngx_memcpy(p, src[i].key.data, src[i].key.len);

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = &src[i].value;

        sc.flushes = &params->flushes;

        sc.lengths = &params->lengths;

        sc.values = &params->values;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_ERROR;

        }

        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

        code = ngx_array_push_n(params->values, sizeof(uintptr_t));

        if (code == NULL) {

            return NGX_ERROR;

        }

        *code = (uintptr_t) NULL;

    }

    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));

    if (code == NULL) {

        return NGX_ERROR;

    }

    *code = (uintptr_t) NULL;

    params->number = headers_names.nelts;

    hash.hash = &params->hash;

    hash.key = ngx_hash_key_lc;

    hash.max_size = 512;

    hash.bucket_size = 64;

    hash.name = "uwsgi_params_hash";

    hash.pool = cf->pool;

    hash.temp_pool = NULL;

    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);

}

static char *

‌ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_uwsgi_loc_conf_t *uwcf = conf;

    size_t                      add;

    ngx_url_t                   u;

    ngx_str_t                  *value, *url;

    ngx_uint_t                  n;

    ngx_http_core_loc_conf_t   *clcf;

    ngx_http_script_compile_t   sc;

    if (uwcf->upstream.upstream || uwcf->uwsgi_lengths) {

        return "is duplicate";

    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_uwsgi_handler;

    value = cf->args->elts;

    url = &value[1];

    n = ngx_http_script_variables_count(url);

    if (n) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;

        sc.source = url;

        sc.lengths = &uwcf->uwsgi_lengths;

        sc.values = &uwcf->uwsgi_values;

        sc.variables = n;

        sc.complete_lengths = 1;

        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

#if (NGX_HTTP_SSL)

        uwcf->ssl = 1;

#endif

        return NGX_CONF_OK;

    }

    if (ngx_strncasecmp(url->data, (u_char *) "uwsgi://", 8) == 0) {

        add = 8;

    } else if (ngx_strncasecmp(url->data, (u_char *) "suwsgi://", 9) == 0) {

#if (NGX_HTTP_SSL)

        add = 9;

        uwcf->ssl = 1;

#else

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                           "suwsgi protocol requires SSL support");

        return NGX_CONF_ERROR;

#endif

    } else {

        add = 0;

    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url.len = url->len - add;

    u.url.data = url->data + add;

    u.no_resolve = 1;

    uwcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);

    if (uwcf->upstream.upstream == NULL) {

        return NGX_CONF_ERROR;

    }

    if (clcf->name.data[clcf->name.len - 1] == '/') {

        clcf->auto_redirect = 1;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_uwsgi_loc_conf_t *uwcf = conf;

    ngx_str_t                  *value;

    ngx_http_script_compile_t   sc;

    if (uwcf->upstream.store != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {

        uwcf->upstream.store = 0;

        return NGX_CONF_OK;

    }

#if (NGX_HTTP_CACHE)

    if (uwcf->upstream.cache > 0) {

        return "is incompatible with \"uwsgi_cache\"";

    }

#endif

    uwcf->upstream.store = 1;

    if (ngx_strcmp(value[1].data, "on") == 0) {

        return NGX_CONF_OK;

    }

    /* include the terminating '\0' into script */

    value[1].len++;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;

    sc.source = &value[1];

    sc.lengths = &uwcf->upstream.store_lengths;

    sc.values = &uwcf->upstream.store_values;

    sc.variables = ngx_http_script_variables_count(&value[1]);

    sc.complete_lengths = 1;

    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#if (NGX_HTTP_CACHE)

static char *

‌ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_uwsgi_loc_conf_t *uwcf = conf;

    ngx_str_t                         *value;

    ngx_http_complex_value_t           cv;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (uwcf->upstream.cache != NGX_CONF_UNSET) {

        return "is duplicate";

    }

    if (ngx_strcmp(value[1].data, "off") == 0) {

        uwcf->upstream.cache = 0;

        return NGX_CONF_OK;

    }

    if (uwcf->upstream.store > 0) {

        return "is incompatible with \"uwsgi_store\"";

    }

    uwcf->upstream.cache = 1;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    if (cv.lengths != NULL) {

        uwcf->upstream.cache_value = ngx_palloc(cf->pool,

                                             sizeof(ngx_http_complex_value_t));

        if (uwcf->upstream.cache_value == NULL) {

            return NGX_CONF_ERROR;

        }

        *uwcf->upstream.cache_value = cv;

        return NGX_CONF_OK;

    }

    uwcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,

                                                      &ngx_http_uwsgi_module);

    if (uwcf->upstream.cache_zone == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_uwsgi_loc_conf_t *uwcf = conf;

    ngx_str_t                         *value;

    ngx_http_compile_complex_value_t   ccv;

    value = cf->args->elts;

    if (uwcf->cache_key.value.data) {

        return "is duplicate";

    }

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[1];

    ccv.complex_value = &uwcf->cache_key;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

#endif

#if (NGX_HTTP_SSL)

static char *

‌ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_uwsgi_loc_conf_t *uwcf = conf;

    ngx_str_t  *value;

    if (uwcf->ssl_passwords != NGX_CONF_UNSET_PTR) {

        return "is duplicate";

    }

    value = cf->args->elts;

    uwcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);

    if (uwcf->ssl_passwords == NULL) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)

{

    ngx_pool_cleanup_t  *cln;

    uwcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));

    if (uwcf->upstream.ssl == NULL) {

        return NGX_ERROR;

    }

    uwcf->upstream.ssl->log = cf->log;

    if (ngx_ssl_create(uwcf->upstream.ssl, uwcf->ssl_protocols, NULL)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_ERROR;

    }

    cln->handler = ngx_ssl_cleanup_ctx;

    cln->data = uwcf->upstream.ssl;

    if (uwcf->ssl_certificate.len) {

        if (uwcf->ssl_certificate_key.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                          "no \"uwsgi_ssl_certificate_key\" is defined "

                          "for certificate \"%V\"", &uwcf->ssl_certificate);

            return NGX_ERROR;

        }

        if (ngx_ssl_certificate(cf, uwcf->upstream.ssl, &uwcf->ssl_certificate,

                                &uwcf->ssl_certificate_key, uwcf->ssl_passwords)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

    }

    if (SSL_CTX_set_cipher_list(uwcf->upstream.ssl->ctx,

                                (const char *) uwcf->ssl_ciphers.data)

        == 0)

    {

        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,

                      "SSL_CTX_set_cipher_list(\"%V\") failed",

                      &uwcf->ssl_ciphers);

        return NGX_ERROR;

    }

    if (uwcf->upstream.ssl_verify) {

        if (uwcf->ssl_trusted_certificate.len == 0) {

            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,

                      "no uwsgi_ssl_trusted_certificate for uwsgi_ssl_verify");

            return NGX_ERROR;

        }

        if (ngx_ssl_trusted_certificate(cf, uwcf->upstream.ssl,

                                        &uwcf->ssl_trusted_certificate,

                                        uwcf->ssl_verify_depth)

            != NGX_OK)

        {

            return NGX_ERROR;

        }

        if (ngx_ssl_crl(cf, uwcf->upstream.ssl, &uwcf->ssl_crl) != NGX_OK) {

            return NGX_ERROR;

        }

    }

    return NGX_OK;

}

#endif

src/http/modules/ngx_http_xslt_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_next_body_filter

ngx_http_next_header_filter

ngx_http_xslt_default_types

ngx_http_xslt_filter_commands

ngx_http_xslt_filter_module

ngx_http_xslt_filter_module_ctx

Data types defined

ngx_http_xslt_file_t

ngx_http_xslt_filter_ctx_t

ngx_http_xslt_filter_loc_conf_t

ngx_http_xslt_filter_main_conf_t

ngx_http_xslt_param_t

ngx_http_xslt_sheet_t

Functions defined

ngx_http_xslt_add_chunk

ngx_http_xslt_apply_stylesheet

ngx_http_xslt_body_filter

ngx_http_xslt_cleanup

ngx_http_xslt_cleanup_dtd

ngx_http_xslt_cleanup_stylesheet

ngx_http_xslt_content_type

ngx_http_xslt_encoding

ngx_http_xslt_entities

ngx_http_xslt_filter_create_conf

ngx_http_xslt_filter_create_main_conf

ngx_http_xslt_filter_exit

ngx_http_xslt_filter_init

ngx_http_xslt_filter_merge_conf

ngx_http_xslt_filter_preconfiguration

ngx_http_xslt_header_filter

ngx_http_xslt_param

ngx_http_xslt_params

ngx_http_xslt_sax_error

ngx_http_xslt_sax_external_subset

ngx_http_xslt_send

ngx_http_xslt_stylesheet

Macros defined

NGX_HTTP_XSLT_REUSE_DTD

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

#include <libxml/parser.h>

#include <libxml/tree.h>

#include <libxslt/xslt.h>

#include <libxslt/xsltInternals.h>

#include <libxslt/transform.h>

#include <libxslt/variables.h>

#include <libxslt/xsltutils.h>

#if (NGX_HAVE_EXSLT)

#include <libexslt/exslt.h>

#endif

#ifndef NGX_HTTP_XSLT_REUSE_DTD

‌#define NGX_HTTP_XSLT_REUSE_DTD  1

#endif

typedef struct {

    u_char                    *name;

    void                      *data;

‌} ngx_http_xslt_file_t;

typedef struct {

    ngx_array_t                dtd_files;    /* ngx_http_xslt_file_t */

    ngx_array_t                sheet_files;  /* ngx_http_xslt_file_t */

‌} ngx_http_xslt_filter_main_conf_t;

typedef struct {

    u_char                    *name;

    ngx_http_complex_value_t   value;

    ngx_uint_t                 quote;        /* unsigned  quote:1; */

‌} ngx_http_xslt_param_t;

typedef struct {

    xsltStylesheetPtr          stylesheet;

    ngx_array_t                params;       /* ngx_http_xslt_param_t */

‌} ngx_http_xslt_sheet_t;

typedef struct {

    xmlDtdPtr                  dtd;

    ngx_array_t                sheets;       /* ngx_http_xslt_sheet_t */

    ngx_hash_t                 types;

    ngx_array_t               *types_keys;

    ngx_array_t               *params;       /* ngx_http_xslt_param_t */

    ngx_flag_t                 last_modified;

‌} ngx_http_xslt_filter_loc_conf_t;

typedef struct {

    xmlDocPtr                  doc;

    xmlParserCtxtPtr           ctxt;

    xsltTransformContextPtr    transform;

    ngx_http_request_t        *request;

    ngx_array_t                params;

    ngx_uint_t                 done;         /* unsigned  done:1; */

‌} ngx_http_xslt_filter_ctx_t;

static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,

    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);

static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,

    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);

static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,

    const xmlChar *externalId, const xmlChar *systemId);

static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);

static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,

    ngx_http_xslt_filter_ctx_t *ctx);

static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,

    ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);

static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);

static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);

static void ngx_http_xslt_cleanup(void *data);

static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static void ngx_http_xslt_cleanup_dtd(void *data);

static void ngx_http_xslt_cleanup_stylesheet(void *data);

static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);

static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);

static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,

    void *child);

static ngx_int_t ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf);

static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);

static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);

‌ngx_str_t  ngx_http_xslt_default_types[] = {

    ngx_string("text/xml"),

    ngx_null_string

};

‌static ngx_command_t  ngx_http_xslt_filter_commands[] = {

    { ngx_string("xml_entities"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,

      ngx_http_xslt_entities,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("xslt_stylesheet"),

      NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_xslt_stylesheet,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("xslt_param"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_http_xslt_param,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

    { ngx_string("xslt_string_param"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_http_xslt_param,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      (void *) 1 },

    { ngx_string("xslt_types"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,

      ngx_http_types_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),

      &ngx_http_xslt_default_types[0] },

    { ngx_string("xslt_last_modified"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_xslt_filter_loc_conf_t, last_modified),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {

    ngx_http_xslt_filter_preconfiguration, /* preconfiguration */

    ngx_http_xslt_filter_init,             /* postconfiguration */

    ngx_http_xslt_filter_create_main_conf, /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_xslt_filter_create_conf,      /* create location configuration */

    ngx_http_xslt_filter_merge_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_xslt_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_xslt_filter_module_ctx,      /* module context */

    ngx_http_xslt_filter_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    ngx_http_xslt_filter_exit,             /* exit process */

    ngx_http_xslt_filter_exit,             /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_xslt_header_filter(ngx_http_request_t *r)

{

    ngx_http_xslt_filter_ctx_t       *ctx;

    ngx_http_xslt_filter_loc_conf_t  *conf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "xslt filter header");

    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {

        return ngx_http_next_header_filter(r);

    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

    if (conf->sheets.nelts == 0

        || ngx_http_test_content_type(r, &conf->types) == NULL)

    {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);

    if (ctx) {

        return ngx_http_next_header_filter(r);

    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));

    if (ctx == NULL) {

        return NGX_ERROR;

    }

    ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);

    r->main_filter_need_in_memory = 1;

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    int                          wellFormed;

    ngx_chain_t                 *cl;

    ngx_http_xslt_filter_ctx_t  *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "xslt filter body");

    if (in == NULL) {

        return ngx_http_next_body_filter(r, in);

    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);

    if (ctx == NULL || ctx->done) {

        return ngx_http_next_body_filter(r, in);

    }

    for (cl = in; cl; cl = cl->next) {

        if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {

            if (ctx->ctxt->myDoc) {

#if (NGX_HTTP_XSLT_REUSE_DTD)

                ctx->ctxt->myDoc->extSubset = NULL;

#endif

                xmlFreeDoc(ctx->ctxt->myDoc);

            }

            xmlFreeParserCtxt(ctx->ctxt);

            return ngx_http_xslt_send(r, ctx, NULL);

        }

        if (cl->buf->last_buf || cl->buf->last_in_chain) {

            ctx->doc = ctx->ctxt->myDoc;

#if (NGX_HTTP_XSLT_REUSE_DTD)

            ctx->doc->extSubset = NULL;

#endif

            wellFormed = ctx->ctxt->wellFormed;

            xmlFreeParserCtxt(ctx->ctxt);

            if (wellFormed) {

                return ngx_http_xslt_send(r, ctx,

                                       ngx_http_xslt_apply_stylesheet(r, ctx));

            }

            xmlFreeDoc(ctx->doc);

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "not well formed XML document");

            return ngx_http_xslt_send(r, ctx, NULL);

        }

    }

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,

    ngx_buf_t *b)

{

    ngx_int_t                         rc;

    ngx_chain_t                       out;

    ngx_pool_cleanup_t               *cln;

    ngx_http_xslt_filter_loc_conf_t  *conf;

    ctx->done = 1;

    if (b == NULL) {

        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

    }

    cln = ngx_pool_cleanup_add(r->pool, 0);

    if (cln == NULL) {

        ngx_free(b->pos);

        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,

                                               NGX_HTTP_INTERNAL_SERVER_ERROR);

    }

    if (r == r->main) {

        r->headers_out.content_length_n = b->last - b->pos;

        if (r->headers_out.content_length) {

            r->headers_out.content_length->hash = 0;

            r->headers_out.content_length = NULL;

        }

        conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

        if (!conf->last_modified) {

            ngx_http_clear_last_modified(r);

            ngx_http_clear_etag(r);

        } else {

            ngx_http_weak_etag(r);

        }

    }

    rc = ngx_http_next_header_filter(r);

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

        ngx_free(b->pos);

        return rc;

    }

    cln->handler = ngx_http_xslt_cleanup;

    cln->data = b->pos;

    out.buf = b;

    out.next = NULL;

    return ngx_http_next_body_filter(r, &out);

}

static ngx_int_t

‌ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,

    ngx_buf_t *b)

{

    int               err;

    xmlParserCtxtPtr  ctxt;

    if (ctx->ctxt == NULL) {

        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);

        if (ctxt == NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "xmlCreatePushParserCtxt() failed");

            return NGX_ERROR;

        }

        xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD

                                               |XML_PARSE_NOWARNING);

        ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;

        ctxt->sax->setDocumentLocator = NULL;

        ctxt->sax->error = ngx_http_xslt_sax_error;

        ctxt->sax->fatalError = ngx_http_xslt_sax_error;

        ctxt->sax->_private = ctx;

        ctx->ctxt = ctxt;

        ctx->request = r;

    }

    err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),

                        (b->last_buf) || (b->last_in_chain));

    if (err == 0) {

        b->pos = b->last;

        return NGX_OK;

    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                  "xmlParseChunk() failed, error:%d", err);

    return NGX_ERROR;

}

static void

‌ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,

    const xmlChar *externalId, const xmlChar *systemId)

{

    xmlParserCtxtPtr ctxt = data;

    xmlDocPtr                         doc;

    xmlDtdPtr                         dtd;

    ngx_http_request_t               *r;

    ngx_http_xslt_filter_ctx_t       *ctx;

    ngx_http_xslt_filter_loc_conf_t  *conf;

    ctx = ctxt->sax->_private;

    r = ctx->request;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",

                   name ? name : (xmlChar *) "",

                   externalId ? externalId : (xmlChar *) "",

                   systemId ? systemId : (xmlChar *) "");

    doc = ctxt->myDoc;

#if (NGX_HTTP_XSLT_REUSE_DTD)

    dtd = conf->dtd;

#else

    dtd = xmlCopyDtd(conf->dtd);

    if (dtd == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "xmlCopyDtd() failed");

        return;

    }

    if (doc->children == NULL) {

        xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);

    } else {

        xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);

    }

#endif

    doc->extSubset = dtd;

}

static void ngx_cdecl

‌ngx_http_xslt_sax_error(void *data, const char *msg, ...)

{

    xmlParserCtxtPtr ctxt = data;

    size_t                       n;

    va_list                      args;

    ngx_http_xslt_filter_ctx_t  *ctx;

    u_char                       buf[NGX_MAX_ERROR_STR];

    ctx = ctxt->sax->_private;

    buf[0] = '\0';

    va_start(args, msg);

    n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);

    va_end(args);

    while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }

    ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,

                  "libxml2 error: \"%*s\"", n + 1, buf);

}

static ngx_buf_t *

‌ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,

    ngx_http_xslt_filter_ctx_t *ctx)

{

    int                               len, rc, doc_type;

    u_char                           *type, *encoding;

    ngx_buf_t                        *b;

    ngx_uint_t                        i;

    xmlChar                          *buf;

    xmlDocPtr                         doc, res;

    ngx_http_xslt_sheet_t            *sheet;

    ngx_http_xslt_filter_loc_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

    sheet = conf->sheets.elts;

    doc = ctx->doc;

    /* preallocate array for 4 params */

    if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))

        != NGX_OK)

    {

        xmlFreeDoc(doc);

        return NULL;

    }

    for (i = 0; i < conf->sheets.nelts; i++) {

        ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);

        if (ctx->transform == NULL) {

            xmlFreeDoc(doc);

            return NULL;

        }

        if (conf->params

            && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)

        {

            xsltFreeTransformContext(ctx->transform);

            xmlFreeDoc(doc);

            return NULL;

        }

        if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {

            xsltFreeTransformContext(ctx->transform);

            xmlFreeDoc(doc);

            return NULL;

        }

        res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,

                                      ctx->params.elts, NULL, NULL,

                                      ctx->transform);

        xsltFreeTransformContext(ctx->transform);

        xmlFreeDoc(doc);

        if (res == NULL) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                          "xsltApplyStylesheet() failed");

            return NULL;

        }

        doc = res;

        /* reset array elements */

        ctx->params.nelts = 0;

    }

    /* there must be at least one stylesheet */

    if (r == r->main) {

        type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);

    } else {

        type = NULL;

    }

    encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);

    doc_type = doc->type;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "xslt filter type: %d t:%s e:%s",

                   doc_type, type ? type : (u_char *) "(null)",

                   encoding ? encoding : (u_char *) "(null)");

    rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);

    xmlFreeDoc(doc);

    if (rc != 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "xsltSaveResultToString() failed");

        return NULL;

    }

    if (len == 0) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                      "xsltSaveResultToString() returned zero-length result");

        return NULL;

    }

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

    if (b == NULL) {

        ngx_free(buf);

        return NULL;

    }

    b->pos = buf;

    b->last = buf + len;

    b->memory = 1;

    if (encoding) {

        r->headers_out.charset.len = ngx_strlen(encoding);

        r->headers_out.charset.data = encoding;

    }

    if (r != r->main) {

        return b;

    }

    b->last_buf = 1;

    if (type) {

        len = ngx_strlen(type);

        r->headers_out.content_type_len = len;

        r->headers_out.content_type.len = len;

        r->headers_out.content_type.data = type;

    } else if (doc_type == XML_HTML_DOCUMENT_NODE) {

        r->headers_out.content_type_len = sizeof("text/html") - 1;

        ngx_str_set(&r->headers_out.content_type, "text/html");

    }

    r->headers_out.content_type_lowcase = NULL;

    return b;

}

static ngx_int_t

‌ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,

    ngx_array_t *params, ngx_uint_t final)

{

    u_char                 *p, *last, *value, *dst, *src, **s;

    size_t                  len;

    ngx_uint_t              i;

    ngx_str_t               string;

    ngx_http_xslt_param_t  *param;

    param = params->elts;

    for (i = 0; i < params->nelts; i++) {

        if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {

            return NGX_ERROR;

        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                       "xslt filter param: \"%s\"", string.data);

        if (param[i].name) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "xslt filter param name: \"%s\"", param[i].name);

            if (param[i].quote) {

                if (xsltQuoteOneUserParam(ctx->transform, param[i].name,

                                          string.data)

                    != 0)

                {

                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",

                                param[i].name, string.data);

                    return NGX_ERROR;

                }

                continue;

            }

            s = ngx_array_push(&ctx->params);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = param[i].name;

            s = ngx_array_push(&ctx->params);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = string.data;

            continue;

        }

        /*

         * parse param1=value1:param2=value2 syntax as used by parameters

         * specified in xslt_stylesheet directives

         */

        p = string.data;

        last = string.data + string.len;

        while (p && *p) {

            value = p;

            p = (u_char *) ngx_strchr(p, '=');

            if (p == NULL) {

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,

                                "invalid libxslt parameter \"%s\"", value);

                return NGX_ERROR;

            }

            *p++ = '\0';

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "xslt filter param name: \"%s\"", value);

            s = ngx_array_push(&ctx->params);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = value;

            value = p;

            p = (u_char *) ngx_strchr(p, ':');

            if (p) {

                len = p - value;

                *p++ = '\0';

            } else {

                len = last - value;

            }

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "xslt filter param value: \"%s\"", value);

            dst = value;

            src = value;

            ngx_unescape_uri(&dst, &src, len, 0);

            *dst = '\0';

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                           "xslt filter param unescaped: \"%s\"", value);

            s = ngx_array_push(&ctx->params);

            if (s == NULL) {

                return NGX_ERROR;

            }

            *s = value;

        }

    }

    if (final) {

        s = ngx_array_push(&ctx->params);

        if (s == NULL) {

            return NGX_ERROR;

        }

        *s = NULL;

    }

    return NGX_OK;

}

static u_char *

‌ngx_http_xslt_content_type(xsltStylesheetPtr s)

{

    u_char  *type;

    if (s->mediaType) {

        return s->mediaType;

    }

    for (s = s->imports; s; s = s->next) {

        type = ngx_http_xslt_content_type(s);

        if (type) {

            return type;

        }

    }

    return NULL;

}

static u_char *

‌ngx_http_xslt_encoding(xsltStylesheetPtr s)

{

    u_char  *encoding;

    if (s->encoding) {

        return s->encoding;

    }

    for (s = s->imports; s; s = s->next) {

        encoding = ngx_http_xslt_encoding(s);

        if (encoding) {

            return encoding;

        }

    }

    return NULL;

}

static void

‌ngx_http_xslt_cleanup(void *data)

{

    ngx_free(data);

}

static char *

‌ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_xslt_filter_loc_conf_t *xlcf = conf;

    ngx_str_t                         *value;

    ngx_uint_t                         i;

    ngx_pool_cleanup_t                *cln;

    ngx_http_xslt_file_t              *file;

    ngx_http_xslt_filter_main_conf_t  *xmcf;

    if (xlcf->dtd) {

        return "is duplicate";

    }

    value = cf->args->elts;

    xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);

    file = xmcf->dtd_files.elts;

    for (i = 0; i < xmcf->dtd_files.nelts; i++) {

        if (ngx_strcmp(file[i].name, value[1].data) == 0) {

            xlcf->dtd = file[i].data;

            return NGX_CONF_OK;

        }

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_CONF_ERROR;

    }

    xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);

    if (xlcf->dtd == NULL) {

        ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");

        return NGX_CONF_ERROR;

    }

    cln->handler = ngx_http_xslt_cleanup_dtd;

    cln->data = xlcf->dtd;

    file = ngx_array_push(&xmcf->dtd_files);

    if (file == NULL) {

        return NGX_CONF_ERROR;

    }

    file->name = value[1].data;

    file->data = xlcf->dtd;

    return NGX_CONF_OK;

}

static char *

‌ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_xslt_filter_loc_conf_t *xlcf = conf;

    ngx_str_t                         *value;

    ngx_uint_t                         i, n;

    ngx_pool_cleanup_t                *cln;

    ngx_http_xslt_file_t              *file;

    ngx_http_xslt_sheet_t             *sheet;

    ngx_http_xslt_param_t             *param;

    ngx_http_compile_complex_value_t   ccv;

    ngx_http_xslt_filter_main_conf_t  *xmcf;

    value = cf->args->elts;

    if (xlcf->sheets.elts == NULL) {

        if (ngx_array_init(&xlcf->sheets, cf->pool, 1,

                           sizeof(ngx_http_xslt_sheet_t))

            != NGX_OK)

        {

            return NGX_CONF_ERROR;

        }

    }

    sheet = ngx_array_push(&xlcf->sheets);

    if (sheet == NULL) {

        return NGX_CONF_ERROR;

    }

    ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));

    if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);

    file = xmcf->sheet_files.elts;

    for (i = 0; i < xmcf->sheet_files.nelts; i++) {

        if (ngx_strcmp(file[i].name, value[1].data) == 0) {

            sheet->stylesheet = file[i].data;

            goto found;

        }

    }

    cln = ngx_pool_cleanup_add(cf->pool, 0);

    if (cln == NULL) {

        return NGX_CONF_ERROR;

    }

    sheet->stylesheet = xsltParseStylesheetFile(value[1].data);

    if (sheet->stylesheet == NULL) {

        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,

                           "xsltParseStylesheetFile(\"%s\") failed",

                           value[1].data);

        return NGX_CONF_ERROR;

    }

    cln->handler = ngx_http_xslt_cleanup_stylesheet;

    cln->data = sheet->stylesheet;

    file = ngx_array_push(&xmcf->sheet_files);

    if (file == NULL) {

        return NGX_CONF_ERROR;

    }

    file->name = value[1].data;

    file->data = sheet->stylesheet;

found:

    n = cf->args->nelts;

    if (n == 2) {

        return NGX_CONF_OK;

    }

    if (ngx_array_init(&sheet->params, cf->pool, n - 2,

                       sizeof(ngx_http_xslt_param_t))

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    for (i = 2; i < n; i++) {

        param = ngx_array_push(&sheet->params);

        if (param == NULL) {

            return NGX_CONF_ERROR;

        }

        ngx_memzero(param, sizeof(ngx_http_xslt_param_t));

        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

        ccv.cf = cf;

        ccv.value = &value[i];

        ccv.complex_value = &param->value;

        ccv.zero = 1;

        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

            return NGX_CONF_ERROR;

        }

    }

    return NGX_CONF_OK;

}

static char *

‌ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;

    ngx_http_xslt_param_t            *param;

    ngx_http_compile_complex_value_t  ccv;

    ngx_str_t                        *value;

    value = cf->args->elts;

    if (xlcf->params == NULL) {

        xlcf->params = ngx_array_create(cf->pool, 2,

                                        sizeof(ngx_http_xslt_param_t));

        if (xlcf->params == NULL) {

            return NGX_CONF_ERROR;

        }

    }

    param = ngx_array_push(xlcf->params);

    if (param == NULL) {

        return NGX_CONF_ERROR;

    }

    param->name = value[1].data;

    param->quote = (cmd->post == NULL) ? 0 : 1;

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;

    ccv.value = &value[2];

    ccv.complex_value = &param->value;

    ccv.zero = 1;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

        return NGX_CONF_ERROR;

    }

    return NGX_CONF_OK;

}

static void

‌ngx_http_xslt_cleanup_dtd(void *data)

{

    xmlFreeDtd(data);

}

static void

‌ngx_http_xslt_cleanup_stylesheet(void *data)

{

    xsltFreeStylesheet(data);

}

static void *

‌ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)

{

    ngx_http_xslt_filter_main_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    if (ngx_array_init(&conf->dtd_files, cf->pool, 1,

                       sizeof(ngx_http_xslt_file_t))

        != NGX_OK)

    {

        return NULL;

    }

    if (ngx_array_init(&conf->sheet_files, cf->pool, 1,

                       sizeof(ngx_http_xslt_file_t))

        != NGX_OK)

    {

        return NULL;

    }

    return conf;

}

static void *

‌ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)

{

    ngx_http_xslt_filter_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc():

     *

     *     conf->dtd = NULL;

     *     conf->sheets = { NULL };

     *     conf->types = { NULL };

     *     conf->types_keys = NULL;

     *     conf->params = NULL;

     */

    conf->last_modified = NGX_CONF_UNSET;

    return conf;

}

static char *

‌ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_xslt_filter_loc_conf_t *prev = parent;

    ngx_http_xslt_filter_loc_conf_t *conf = child;

    if (conf->dtd == NULL) {

        conf->dtd = prev->dtd;

    }

    if (conf->sheets.nelts == 0) {

        conf->sheets = prev->sheets;

    }

    if (conf->params == NULL) {

        conf->params = prev->params;

    }

    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,

                             &prev->types_keys, &prev->types,

                             ngx_http_xslt_default_types)

        != NGX_OK)

    {

        return NGX_CONF_ERROR;

    }

    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

    return NGX_CONF_OK;

}

static ngx_int_t

‌ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)

{

    xmlInitParser();

#if (NGX_HAVE_EXSLT)

    exsltRegisterAll();

#endif

    return NGX_OK;

}

static ngx_int_t

‌ngx_http_xslt_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_xslt_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_xslt_body_filter;

    return NGX_OK;

}

static void

‌ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)

{

    xsltCleanupGlobals();

    xmlCleanupParser();

}

src/http/modules/perl/ - nginx-1.7.9

nginx.pm

ngx_http_perl_module.c

ngx_http_perl_module.h

src/http/modules/perl/nginx.pm - nginx-1.7.9

Data types defined

rflush

Source code

package nginx;

use 5.006001;

use strict;

use warnings;

require Exporter;

our @ISA = qw(Exporter);

our @EXPORT = qw(

    OK

    DECLINED

    HTTP_OK

    HTTP_CREATED

    HTTP_ACCEPTED

    HTTP_NO_CONTENT

    HTTP_PARTIAL_CONTENT

    HTTP_MOVED_PERMANENTLY

    HTTP_MOVED_TEMPORARILY

    HTTP_REDIRECT

    HTTP_SEE_OTHER

    HTTP_NOT_MODIFIED

    HTTP_TEMPORARY_REDIRECT

    HTTP_BAD_REQUEST

    HTTP_UNAUTHORIZED

    HTTP_PAYMENT_REQUIRED

    HTTP_FORBIDDEN

    HTTP_NOT_FOUND

    HTTP_NOT_ALLOWED

    HTTP_NOT_ACCEPTABLE

    HTTP_REQUEST_TIME_OUT

    HTTP_CONFLICT

    HTTP_GONE

    HTTP_LENGTH_REQUIRED

    HTTP_REQUEST_ENTITY_TOO_LARGE

    HTTP_REQUEST_URI_TOO_LARGE

    HTTP_UNSUPPORTED_MEDIA_TYPE

    HTTP_RANGE_NOT_SATISFIABLE

    HTTP_INTERNAL_SERVER_ERROR

    HTTP_SERVER_ERROR

    HTTP_NOT_IMPLEMENTED

    HTTP_BAD_GATEWAY

    HTTP_SERVICE_UNAVAILABLE

    HTTP_GATEWAY_TIME_OUT

    HTTP_INSUFFICIENT_STORAGE

);

our $VERSION = '%%VERSION%%';

require XSLoader;

XSLoader::load('nginx', $VERSION);

# Preloaded methods go here.

use constant OK                             => 0;

use constant DECLINED                       => -5;

use constant HTTP_OK                        => 200;

use constant HTTP_CREATED                   => 201;

use constant HTTP_ACCEPTED                  => 202;

use constant HTTP_NO_CONTENT                => 204;

use constant HTTP_PARTIAL_CONTENT           => 206;

use constant HTTP_MOVED_PERMANENTLY         => 301;

use constant HTTP_MOVED_TEMPORARILY         => 302;

use constant HTTP_REDIRECT                  => 302;

use constant HTTP_SEE_OTHER                 => 303;

use constant HTTP_NOT_MODIFIED              => 304;

use constant HTTP_TEMPORARY_REDIRECT        => 307;

use constant HTTP_BAD_REQUEST               => 400;

use constant HTTP_UNAUTHORIZED              => 401;

use constant HTTP_PAYMENT_REQUIRED          => 402;

use constant HTTP_FORBIDDEN                 => 403;

use constant HTTP_NOT_FOUND                 => 404;

use constant HTTP_NOT_ALLOWED               => 405;

use constant HTTP_NOT_ACCEPTABLE            => 406;

use constant HTTP_REQUEST_TIME_OUT          => 408;

use constant HTTP_CONFLICT                  => 409;

use constant HTTP_GONE                      => 410;

use constant HTTP_LENGTH_REQUIRED           => 411;

use constant HTTP_REQUEST_ENTITY_TOO_LARGE  => 413;

use constant HTTP_REQUEST_URI_TOO_LARGE     => 414;

use constant HTTP_UNSUPPORTED_MEDIA_TYPE    => 415;

use constant HTTP_RANGE_NOT_SATISFIABLE     => 416;

use constant HTTP_INTERNAL_SERVER_ERROR     => 500;

use constant HTTP_SERVER_ERROR              => 500;

use constant HTTP_NOT_IMPLEMENTED           => 501;

use constant HTTP_BAD_GATEWAY               => 502;

use constant HTTP_SERVICE_UNAVAILABLE       => 503;

use constant HTTP_GATEWAY_TIME_OUT          => 504;

use constant HTTP_INSUFFICIENT_STORAGE      => 507;

‌sub rflush {

    my $r = shift;

    $r->flush;

}

1;

__END__

=head1 NAME

nginx - Perl interface to the nginx HTTP server API

=head1 SYNOPSIS

  use nginx;

=head1 DESCRIPTION

This module provides a Perl interface to the nginx HTTP server API.

=head1 SEE ALSO

http://nginx.org/en/docs/http/ngx_http_perl_module.html

=head1 AUTHOR

Igor Sysoev

=head1 COPYRIGHT AND LICENSE

Copyright (C) Igor Sysoev

Copyright (C) Nginx, Inc.

=cut

src/http/ngx_http_copy_filter_module.c - nginx-1.7.9

Global variables defined

ngx_http_copy_filter_commands

ngx_http_copy_filter_module

ngx_http_copy_filter_module_ctx

ngx_http_next_body_filter

Data types defined

ngx_http_copy_filter_conf_t

Functions defined

ngx_http_copy_aio_event_handler

ngx_http_copy_aio_handler

ngx_http_copy_aio_sendfile_event_handler

ngx_http_copy_filter

ngx_http_copy_filter_create_conf

ngx_http_copy_filter_init

ngx_http_copy_filter_merge_conf

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

typedef struct {

    ngx_bufs_t  bufs;

‌} ngx_http_copy_filter_conf_t;

#if (NGX_HAVE_FILE_AIO)

static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,

    ngx_file_t *file);

static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);

#if (NGX_HAVE_AIO_SENDFILE)

static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);

#endif

#endif

static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);

static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,

    void *parent, void *child);

static ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf);

‌static ngx_command_t  ngx_http_copy_filter_commands[] = {

    { ngx_string("output_buffers"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,

      ngx_conf_set_bufs_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_copy_filter_conf_t, bufs),

      NULL },

      ngx_null_command

};

‌static ngx_http_module_t  ngx_http_copy_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_copy_filter_init,             /* postconfiguration */

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

    ngx_http_copy_filter_create_conf,      /* create location configuration */

    ngx_http_copy_filter_merge_conf        /* merge location configuration */

};

‌ngx_module_t  ngx_http_copy_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_copy_filter_module_ctx,      /* module context */

    ngx_http_copy_filter_commands,         /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

‌static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t

‌ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

    ngx_int_t                     rc;

    ngx_connection_t             *c;

    ngx_output_chain_ctx_t       *ctx;

    ngx_http_core_loc_conf_t     *clcf;

    ngx_http_copy_filter_conf_t  *conf;

    c = r->connection;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,

                   "http copy filter: \"%V?%V\"", &r->uri, &r->args);

    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);

    if (ctx == NULL) {

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));

        if (ctx == NULL) {

            return NGX_ERROR;

        }

        ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);

        conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        ctx->sendfile = c->sendfile;

        ctx->need_in_memory = r->main_filter_need_in_memory

                              || r->filter_need_in_memory;

        ctx->need_in_temp = r->filter_need_temporary;

        ctx->alignment = clcf->directio_alignment;

        ctx->pool = r->pool;

        ctx->bufs = conf->bufs;

        ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;

        ctx->output_filter = (ngx_output_chain_filter_pt)

                                  ngx_http_next_body_filter;

        ctx->filter_ctx = r;

#if (NGX_HAVE_FILE_AIO)

        if (ngx_file_aio) {

            if (clcf->aio) {

                ctx->aio_handler = ngx_http_copy_aio_handler;

            }

#if (NGX_HAVE_AIO_SENDFILE)

            c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE);

#endif

        }

#endif

        if (in && in->buf && ngx_buf_size(in->buf)) {

            r->request_output = 1;

        }

    }

#if (NGX_HAVE_FILE_AIO)

    ctx->aio = r->aio;

#endif

    for ( ;; ) {

        rc = ngx_output_chain(ctx, in);

        if (ctx->in == NULL) {

            r->buffered &= ~NGX_HTTP_COPY_BUFFERED;

        } else {

            r->buffered |= NGX_HTTP_COPY_BUFFERED;

        }

        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,

                       "http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);

#if (NGX_HAVE_FILE_AIO && NGX_HAVE_AIO_SENDFILE)

        if (c->busy_sendfile) {

            ssize_t                n;

            off_t                  offset;

            ngx_file_t            *file;

            ngx_http_ephemeral_t  *e;

            if (r->aio) {

                c->busy_sendfile = NULL;

                return rc;

            }

            file = c->busy_sendfile->file;

            offset = c->busy_sendfile->file_pos;

            if (file->aio) {

                c->busy_count = (offset == file->aio->last_offset) ?

                                c->busy_count + 1 : 0;

                file->aio->last_offset = offset;

                if (c->busy_count > 2) {

                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                                  "sendfile(%V) returned busy again",

                                  &file->name);

                    c->aio_sendfile = 0;

                }

            }

            c->busy_sendfile = NULL;

            e = (ngx_http_ephemeral_t *) &r->uri_start;

            n = ngx_file_aio_read(file, &e->aio_preload, 1, offset, r->pool);

            if (n > 0) {

                in = NULL;

                continue;

            }

            rc = n;

            if (rc == NGX_AGAIN) {

                file->aio->data = r;

                file->aio->handler = ngx_http_copy_aio_sendfile_event_handler;

                r->main->blocked++;

                r->aio = 1;

            }

        }

#endif

        return rc;

    }

}

#if (NGX_HAVE_FILE_AIO)

static void

‌ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)

{

    ngx_http_request_t *r;

    r = ctx->filter_ctx;

    file->aio->data = r;

    file->aio->handler = ngx_http_copy_aio_event_handler;

    r->main->blocked++;

    r->aio = 1;

    ctx->aio = 1;

}

static void

‌ngx_http_copy_aio_event_handler(ngx_event_t *ev)

{

    ngx_event_aio_t     *aio;

    ngx_http_request_t  *r;

    aio = ev->data;

    r = aio->data;

    r->main->blocked--;

    r->aio = 0;

    r->connection->write->handler(r->connection->write);

}

#if (NGX_HAVE_AIO_SENDFILE)

static void

‌ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)

{

    ngx_event_aio_t     *aio;

    ngx_http_request_t  *r;

    aio = ev->data;

    r = aio->data;

    r->main->blocked--;

    r->aio = 0;

    ev->complete = 0;

    r->connection->write->handler(r->connection->write);

}

#endif

#endif

static void *

‌ngx_http_copy_filter_create_conf(ngx_conf_t *cf)

{

    ngx_http_copy_filter_conf_t *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t));

    if (conf == NULL) {

        return NULL;

    }

    conf->bufs.num = 0;

    return conf;

}

static char *

‌ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ngx_http_copy_filter_conf_t *prev = parent;

    ngx_http_copy_filter_conf_t *conf = child;

    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);

    return NULL;

}

static ngx_int_t

‌ngx_http_copy_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_copy_filter;

    return NGX_OK;

}

src/mail/ - nginx-1.7.9

ngx_mail.c

ngx_mail.h

ngx_mail_auth_http_module.c

ngx_mail_core_module.c

ngx_mail_handler.c

ngx_mail_imap_handler.c

ngx_mail_imap_module.c

ngx_mail_imap_module.h

ngx_mail_parse.c

ngx_mail_pop3_handler.c

ngx_mail_pop3_module.c

ngx_mail_pop3_module.h

ngx_mail_proxy_module.c

ngx_mail_smtp_handler.c

ngx_mail_smtp_module.c

ngx_mail_smtp_module.h

ngx_mail_ssl_module.c

ngx_mail_ssl_module.h

src/misc/ - nginx-1.7.9

ngx_cpp_test_module.cpp

ngx_google_perftools_module.c

src/misc/ngx_cpp_test_module.cpp - nginx-1.7.9

Functions defined

ngx_cpp_test_handler

Source code

// stub module to test header files' C++ compatibility

extern "C" {

  #include <ngx_config.h>

  #include <ngx_core.h>

  #include <ngx_event.h>

  #include <ngx_event_connect.h>

  #include <ngx_event_pipe.h>

  #include <ngx_http.h>

  #include <ngx_mail.h>

  #include <ngx_mail_pop3_module.h>

  #include <ngx_mail_imap_module.h>

  #include <ngx_mail_smtp_module.h>

}

// nginx header files should go before other, because they define 64-bit off_t

// #include <string>

void ngx_cpp_test_handler(void *data);

void

‌ngx_cpp_test_handler(void *data)

{

    return;

}

src/misc/ngx_google_perftools_module.c - nginx-1.7.9

Global variables defined

ngx_google_perftools_commands

ngx_google_perftools_module

ngx_google_perftools_module_ctx

Data types defined

ngx_google_perftools_conf_t

Functions defined

ngx_google_perftools_create_conf

ngx_google_perftools_worker

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

/*

* declare Profiler interface here because

* <google/profiler.h> is C++ header file

*/

int ProfilerStart(u_char* fname);

void ProfilerStop(void);

void ProfilerRegisterThread(void);

static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle);

static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle);

typedef struct {

    ngx_str_t  profiles;

‌} ngx_google_perftools_conf_t;

‌static ngx_command_t  ngx_google_perftools_commands[] = {

    { ngx_string("google_perftools_profiles"),

      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,

      ngx_conf_set_str_slot,

      0,

      offsetof(ngx_google_perftools_conf_t, profiles),

      NULL },

    ngx_null_command

};

‌static ngx_core_module_t  ngx_google_perftools_module_ctx = {

    ngx_string("google_perftools"),

    ngx_google_perftools_create_conf,

    NULL

};

‌ngx_module_t  ngx_google_perftools_module = {

    NGX_MODULE_V1,

    &ngx_google_perftools_module_ctx,      /* module context */

    ngx_google_perftools_commands,         /* module directives */

    NGX_CORE_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    ngx_google_perftools_worker,           /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

static void *

‌ngx_google_perftools_create_conf(ngx_cycle_t *cycle)

{

    ngx_google_perftools_conf_t  *gptcf;

    gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t));

    if (gptcf == NULL) {

        return NULL;

    }

    /*

     * set by ngx_pcalloc()

     *

     *     gptcf->profiles = { 0, NULL };

     */

    return gptcf;

}

static ngx_int_t

‌ngx_google_perftools_worker(ngx_cycle_t *cycle)

{

    u_char                       *profile;

    ngx_google_perftools_conf_t  *gptcf;

    gptcf = (ngx_google_perftools_conf_t *)

                ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module);

    if (gptcf->profiles.len == 0) {

        return NGX_OK;

    }

    profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log);

    if (profile == NULL) {

        return NGX_OK;

    }

    if (getenv("CPUPROFILE")) {

        /* disable inherited Profiler enabled in master process */

        ProfilerStop();

    }

    ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid);

    if (ProfilerStart(profile)) {

        /* start ITIMER_PROF timer */

        ProfilerRegisterThread();

    } else {

        ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,

                      "ProfilerStart(%s) failed", profile);

    }

    ngx_free(profile);

    return NGX_OK;

}

/* ProfilerStop() is called on Profiler destruction */

src/os/ - nginx-1.7.9

unix/

src/os/unix/ - nginx-1.7.9

ngx_aio_read.c

ngx_aio_read_chain.c

ngx_aio_write.c

ngx_aio_write_chain.c

ngx_alloc.c

ngx_alloc.h

ngx_atomic.h

ngx_channel.c

ngx_channel.h

ngx_daemon.c

ngx_darwin.h

ngx_darwin_config.h

ngx_darwin_init.c

ngx_darwin_sendfile_chain.c

ngx_errno.c

ngx_errno.h

ngx_file_aio_read.c

ngx_files.c

ngx_files.h

ngx_freebsd.h

ngx_freebsd_config.h

ngx_freebsd_init.c

ngx_freebsd_rfork_thread.c

ngx_freebsd_rfork_thread.h

ngx_freebsd_sendfile_chain.c

ngx_gcc_atomic_amd64.h

ngx_gcc_atomic_ppc.h

ngx_gcc_atomic_sparc64.h

ngx_gcc_atomic_x86.h

ngx_linux.h

ngx_linux_aio_read.c

ngx_linux_config.h

ngx_linux_init.c

ngx_linux_sendfile_chain.c

ngx_os.h

ngx_posix_config.h

ngx_posix_init.c

ngx_process.c

ngx_process.h

ngx_process_cycle.c

ngx_process_cycle.h

ngx_pthread_thread.c

ngx_readv_chain.c

ngx_recv.c

ngx_send.c

ngx_setaffinity.c

ngx_setaffinity.h

ngx_setproctitle.c

ngx_setproctitle.h

ngx_shmem.c

ngx_shmem.h

ngx_socket.c

ngx_socket.h

ngx_solaris.h

ngx_solaris_config.h

ngx_solaris_init.c

ngx_solaris_sendfilev_chain.c

ngx_sunpro_atomic_sparc64.h

ngx_thread.h

ngx_time.c

ngx_time.h

ngx_udp_recv.c

ngx_user.c

ngx_user.h

ngx_writev_chain.c

rfork_thread.S

src/os/unix/ngx_darwin.h - nginx-1.7.9

Macros defined

_NGX_DARWIN_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_DARWIN_H_INCLUDED_

‌#define _NGX_DARWIN_H_INCLUDED_

void ngx_debug_init(void);

ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

extern int       ngx_darwin_kern_osreldate;

extern int       ngx_darwin_hw_ncpu;

extern u_long    ngx_darwin_net_inet_tcp_sendspace;

extern ngx_uint_t  ngx_debug_malloc;

#endif /* _NGX_DARWIN_H_INCLUDED_ */

src/os/unix/ngx_darwin_sendfile_chain.c - nginx-1.7.9

Functions defined

ngx_darwin_sendfile_chain

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_event.h>

/*

* It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same

* old bug as early FreeBSD sendfile() syscall:

* http://bugs.freebsd.org/33771

*

* Besides sendfile() has another bug: if one calls sendfile()

* with both a header and a trailer, then sendfile() ignores a file part

* at all and sends only the header and the trailer together.

* For this reason we send a trailer only if there is no a header.

*

* Although sendfile() allows to pass a header or a trailer,

* it may send the header or the trailer and a part of the file

* in different packets.  And FreeBSD workaround (TCP_NOPUSH option)

* does not help.

*/

ngx_chain_t *

‌ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

{

    int              rc;

    off_t            send, prev_send, sent;

    off_t            file_size;

    ssize_t          n;

    ngx_uint_t       eintr;

    ngx_err_t        err;

    ngx_buf_t       *file;

    ngx_event_t     *wev;

    ngx_chain_t     *cl;

    ngx_iovec_t      header, trailer;

    struct sf_hdtr   hdtr;

    struct iovec     headers[NGX_IOVS_PREALLOCATE];

    struct iovec     trailers[NGX_IOVS_PREALLOCATE];

    wev = c->write;

    if (!wev->ready) {

        return in;

    }

#if (NGX_HAVE_KQUEUE)

    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {

        (void) ngx_connection_error(c, wev->kq_errno,

                               "kevent() reported about an closed connection");

        wev->error = 1;

        return NGX_CHAIN_ERROR;

    }

#endif

    /* the maximum limit size is the maximum size_t value - the page size */

    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {

        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;

    }

    send = 0;

    header.iovs = headers;

    header.nalloc = NGX_IOVS_PREALLOCATE;

    trailer.iovs = trailers;

    trailer.nalloc = NGX_IOVS_PREALLOCATE;

    for ( ;; ) {

        eintr = 0;

        prev_send = send;

        /* create the header iovec and coalesce the neighbouring bufs */

        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);

        if (cl == NGX_CHAIN_ERROR) {

            return NGX_CHAIN_ERROR;

        }

        send += header.size;

        if (cl && cl->buf->in_file && send < limit) {

            file = cl->buf;

            /* coalesce the neighbouring file bufs */

            file_size = ngx_chain_coalesce_file(&cl, limit - send);

            send += file_size;

            if (header.count == 0) {

                /*

                 * create the trailer iovec and coalesce the neighbouring bufs

                 */

                cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,

                                               c->log);

                if (cl == NGX_CHAIN_ERROR) {

                    return NGX_CHAIN_ERROR;

                }

                send += trailer.size;

            }

            /*

             * sendfile() returns EINVAL if sf_hdtr's count is 0,

             * but corresponding pointer is not NULL

             */

            hdtr.headers = header.count ? header.iovs : NULL;

            hdtr.hdr_cnt = header.count;

            hdtr.trailers = trailer.count ? trailer.iovs : NULL;

            hdtr.trl_cnt = trailer.count;

            sent = header.size + file_size;

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "sendfile: @%O %O h:%uz",

                           file->file_pos, sent, header.size);

            rc = sendfile(file->file->fd, c->fd, file->file_pos,

                          &sent, &hdtr, 0);

            if (rc == -1) {

                err = ngx_errno;

                switch (err) {

                case NGX_EAGAIN:

                    break;

                case NGX_EINTR:

                    eintr = 1;

                    break;

                default:

                    wev->error = 1;

                    (void) ngx_connection_error(c, err, "sendfile() failed");

                    return NGX_CHAIN_ERROR;

                }

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,

                               "sendfile() sent only %O bytes", sent);

            }

            if (rc == 0 && sent == 0) {

                /*

                 * if rc and sent equal to zero, then someone

                 * has truncated the file, so the offset became beyond

                 * the end of the file

                 */

                ngx_log_error(NGX_LOG_ALERT, c->log, 0,

                              "sendfile() reported that \"%s\" was truncated",

                              file->file->name.data);

                return NGX_CHAIN_ERROR;

            }

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,

                           "sendfile: %d, @%O %O:%O",

                           rc, file->file_pos, sent, file_size + header.size);

        } else {

            n = ngx_writev(c, &header);

            if (n == NGX_ERROR) {

                return NGX_CHAIN_ERROR;

            }

            sent = (n == NGX_AGAIN) ? 0 : n;

        }

        c->sent += sent;

        in = ngx_chain_update_sent(in, sent);

        if (eintr) {

            send = prev_send + sent;

            continue;

        }

        if (send - prev_send != sent) {

            wev->ready = 0;

            return in;

        }

        if (send >= limit || in == NULL) {

            return in;

        }

    }

}

src/os/unix/ngx_darwin_init.c - nginx-1.7.9

Global variables defined

ngx_darwin_hw_ncpu

ngx_darwin_io

ngx_darwin_kern_ipc_somaxconn

ngx_darwin_kern_osrelease

ngx_darwin_kern_ostype

ngx_darwin_net_inet_tcp_sendspace

ngx_debug_malloc

sysctls

Data types defined

sysctl_t

Functions defined

ngx_debug_init

ngx_os_specific_init

ngx_os_specific_status

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌char    ngx_darwin_kern_ostype[16];

‌char    ngx_darwin_kern_osrelease[128];

‌int     ngx_darwin_hw_ncpu;

‌int     ngx_darwin_kern_ipc_somaxconn;

‌u_long  ngx_darwin_net_inet_tcp_sendspace;

‌ngx_uint_t  ngx_debug_malloc;

‌static ngx_os_io_t ngx_darwin_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

#if (NGX_HAVE_SENDFILE)

    ngx_darwin_sendfile_chain,

    NGX_IO_SENDFILE

#else

    ngx_writev_chain,

    0

#endif

};

typedef struct {

    char        *name;

    void        *value;

    size_t       size;

    ngx_uint_t   exists;

‌} sysctl_t;

‌sysctl_t sysctls[] = {

    { "hw.ncpu",

      &ngx_darwin_hw_ncpu,

      sizeof(ngx_darwin_hw_ncpu), 0 },

    { "net.inet.tcp.sendspace",

      &ngx_darwin_net_inet_tcp_sendspace,

      sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },

    { "kern.ipc.somaxconn",

      &ngx_darwin_kern_ipc_somaxconn,

      sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },

    { NULL, NULL, 0, 0 }

};

void

‌ngx_debug_init(void)

{

#if (NGX_DEBUG_MALLOC)

    /*

     * MacOSX 10.6, 10.7:  MallocScribble fills freed memory with 0x55

     *                     and fills allocated memory with 0xAA.

     * MacOSX 10.4, 10.5:  MallocScribble fills freed memory with 0x55,

     *                     MallocPreScribble fills allocated memory with 0xAA.

     * MacOSX 10.3:        MallocScribble fills freed memory with 0x55,

     *                     and no way to fill allocated memory.

     */

    setenv("MallocScribble", "1", 0);

    ngx_debug_malloc = 1;

#else

    if (getenv("MallocScribble")) {

        ngx_debug_malloc = 1;

    }

#endif

}

ngx_int_t

‌ngx_os_specific_init(ngx_log_t *log)

{

    size_t      size;

    ngx_err_t   err;

    ngx_uint_t  i;

    size = sizeof(ngx_darwin_kern_ostype);

    if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0)

        == -1)

    {

        err = ngx_errno;

        if (err != NGX_ENOENT) {

            ngx_log_error(NGX_LOG_ALERT, log, err,

                          "sysctlbyname(kern.ostype) failed");

            if (err != NGX_ENOMEM) {

                return NGX_ERROR;

            }

            ngx_darwin_kern_ostype[size - 1] = '\0';

        }

    }

    size = sizeof(ngx_darwin_kern_osrelease);

    if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size,

                     NULL, 0)

        == -1)

    {

        err = ngx_errno;

        if (err != NGX_ENOENT) {

            ngx_log_error(NGX_LOG_ALERT, log, err,

                          "sysctlbyname(kern.osrelease) failed");

            if (err != NGX_ENOMEM) {

                return NGX_ERROR;

            }

            ngx_darwin_kern_osrelease[size - 1] = '\0';

        }

    }

    for (i = 0; sysctls[i].name; i++) {

        size = sysctls[i].size;

        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)

            == 0)

        {

            sysctls[i].exists = 1;

            continue;

        }

        err = ngx_errno;

        if (err == NGX_ENOENT) {

            continue;

        }

        ngx_log_error(NGX_LOG_ALERT, log, err,

                      "sysctlbyname(%s) failed", sysctls[i].name);

        return NGX_ERROR;

    }

    ngx_ncpu = ngx_darwin_hw_ncpu;

    if (ngx_darwin_kern_ipc_somaxconn > 32767) {

        ngx_log_error(NGX_LOG_ALERT, log, 0,

                      "sysctl kern.ipc.somaxconn must be less than 32768");

        return NGX_ERROR;

    }

    ngx_tcp_nodelay_and_tcp_nopush = 1;

    ngx_os_io = ngx_darwin_io;

    return NGX_OK;

}

void

‌ngx_os_specific_status(ngx_log_t *log)

{

    u_long      value;

    ngx_uint_t  i;

    if (ngx_darwin_kern_ostype[0]) {

        ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",

                      ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);

    }

    for (i = 0; sysctls[i].name; i++) {

        if (sysctls[i].exists) {

            if (sysctls[i].size == sizeof(long)) {

                value = *(long *) sysctls[i].value;

            } else {

                value = *(int *) sysctls[i].value;

            }

            ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",

                          sysctls[i].name, value);

        }

    }

}

src/os/unix/ngx_freebsd.h - nginx-1.7.9

Macros defined

_NGX_FREEBSD_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_FREEBSD_H_INCLUDED_

‌#define _NGX_FREEBSD_H_INCLUDED_

void ngx_debug_init(void);

ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

extern int         ngx_freebsd_kern_osreldate;

extern int         ngx_freebsd_hw_ncpu;

extern u_long      ngx_freebsd_net_inet_tcp_sendspace;

extern ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;

extern ngx_uint_t  ngx_freebsd_use_tcp_nopush;

extern ngx_uint_t  ngx_debug_malloc;

#endif /* _NGX_FREEBSD_H_INCLUDED_ */

src/os/unix/ngx_gcc_atomic_amd64.h - nginx-1.7.9

Functions defined

ngx_atomic_cmp_set

ngx_atomic_fetch_add

Macros defined

NGX_SMP_LOCK

NGX_SMP_LOCK

ngx_cpu_pause

ngx_memory_barrier

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#if (NGX_SMP)

‌#define NGX_SMP_LOCK  "lock;"

#else

‌#define NGX_SMP_LOCK

#endif

/*

* "cmpxchgq  r, [m]":

*

*     if (rax == [m]) {

*         zf = 1;

*         [m] = r;

*     } else {

*         zf = 0;

*         rax = [m];

*     }

*

*

* The "r" is any register, %rax (%r0) - %r16.

* The "=a" and "a" are the %rax register.

* Although we can return result in any register, we use "a" because it is

* used in cmpxchgq anyway.  The result is actually in %al but not in $rax,

* however as the code is inlined gcc can test %al as well as %rax.

*

* The "cc" means that flags were changed.

*/

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    u_char  res;

    __asm__ volatile (

         NGX_SMP_LOCK

    "    cmpxchgq  %3, %1;   "

    "    sete      %0;       "

    : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");

    return res;

}

/*

* "xaddq  r, [m]":

*

*     temp = [m];

*     [m] += r;

*     r = temp;

*

*

* The "+r" is any register, %rax (%r0) - %r16.

* The "cc" means that flags were changed.

*/

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    __asm__ volatile (

         NGX_SMP_LOCK

    "    xaddq  %0, %1;   "

    : "+r" (add) : "m" (*value) : "cc", "memory");

    return add;

}

‌#define ngx_memory_barrier()    __asm__ volatile ("" ::: "memory")

‌#define ngx_cpu_pause()         __asm__ ("pause")

src/os/unix/ngx_gcc_atomic_x86.h - nginx-1.7.9

Functions defined

ngx_atomic_cmp_set

ngx_atomic_fetch_add

ngx_atomic_fetch_add

Macros defined

NGX_SMP_LOCK

NGX_SMP_LOCK

ngx_cpu_pause

ngx_memory_barrier

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#if (NGX_SMP)

‌#define NGX_SMP_LOCK  "lock;"

#else

‌#define NGX_SMP_LOCK

#endif

/*

* "cmpxchgl  r, [m]":

*

*     if (eax == [m]) {

*         zf = 1;

*         [m] = r;

*     } else {

*         zf = 0;

*         eax = [m];

*     }

*

*

* The "r" means the general register.

* The "=a" and "a" are the %eax register.

* Although we can return result in any register, we use "a" because it is

* used in cmpxchgl anyway.  The result is actually in %al but not in %eax,

* however, as the code is inlined gcc can test %al as well as %eax,

* and icc adds "movzbl %al, %eax" by itself.

*

* The "cc" means that flags were changed.

*/

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    u_char  res;

    __asm__ volatile (

         NGX_SMP_LOCK

    "    cmpxchgl  %3, %1;   "

    "    sete      %0;       "

    : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");

    return res;

}

/*

* "xaddl  r, [m]":

*

*     temp = [m];

*     [m] += r;

*     r = temp;

*

*

* The "+r" means the general register.

* The "cc" means that flags were changed.

*/

#if !(( __GNUC__ == 2 && __GNUC_MINOR__ <= 7 ) || ( __INTEL_COMPILER >= 800 ))

/*

* icc 8.1 and 9.0 compile broken code with -march=pentium4 option:

* ngx_atomic_fetch_add() always return the input "add" value,

* so we use the gcc 2.7 version.

*

* icc 8.1 and 9.0 with -march=pentiumpro option or icc 7.1 compile

* correct code.

*/

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    __asm__ volatile (

         NGX_SMP_LOCK

    "    xaddl  %0, %1;   "

    : "+r" (add) : "m" (*value) : "cc", "memory");

    return add;

}

#else

/*

* gcc 2.7 does not support "+r", so we have to use the fixed

* %eax ("=a" and "a") and this adds two superfluous instructions in the end

* of code, something like this: "mov %eax, %edx / mov %edx, %eax".

*/

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    ngx_atomic_uint_t  old;

    __asm__ volatile (

         NGX_SMP_LOCK

    "    xaddl  %2, %1;   "

    : "=a" (old) : "m" (*value), "a" (add) : "cc", "memory");

    return old;

}

#endif

/*

* on x86 the write operations go in a program order, so we need only

* to disable the gcc reorder optimizations

*/

‌#define ngx_memory_barrier()    __asm__ volatile ("" ::: "memory")

/* old "as" does not support "pause" opcode */

‌#define ngx_cpu_pause()         __asm__ (".byte 0xf3, 0x90")

src/os/unix/ngx_gcc_atomic_ppc.h - nginx-1.7.9

Functions defined

ngx_atomic_cmp_set

ngx_atomic_cmp_set

ngx_atomic_fetch_add

ngx_atomic_fetch_add

Macros defined

ngx_cpu_pause

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

ngx_memory_barrier

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

/*

* The ppc assembler treats ";" as comment, so we have to use "\n".

* The minus in "bne-" is a hint for the branch prediction unit that

* this branch is unlikely to be taken.

* The "1b" means the nearest backward label "1" and the "1f" means

* the nearest forward label "1".

*

* The "b" means that the base registers can be used only, i.e.

* any register except r0.  The r0 register always has a zero value and

* could not be used in "addi  r0, r0, 1".

* The "=&b" means that no input registers can be used.

*

* "sync"    read and write barriers

* "isync"   read barrier, is faster than "sync"

* "eieio"   write barrier, is faster than "sync"

* "lwsync"  write barrier, is faster than "eieio" on ppc64

*/

#if (NGX_PTR_SIZE == 8)

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    ngx_atomic_uint_t  res, temp;

    __asm__ volatile (

    "    li      %0, 0       \n" /* preset "0" to "res"                      */

    "    lwsync              \n" /* write barrier                            */

    "1:                      \n"

    "    ldarx   %1, 0, %2   \n" /* load from [lock] into "temp"             */

                                 /*   and store reservation                  */

    "    cmpd    %1, %3      \n" /* compare "temp" and "old"                 */

    "    bne-    2f          \n" /* not equal                                */

    "    stdcx.  %4, 0, %2   \n" /* store "set" into [lock] if reservation   */

                                 /*   is not cleared                         */

    "    bne-    1b          \n" /* the reservation was cleared              */

    "    isync               \n" /* read barrier                             */

    "    li      %0, 1       \n" /* set "1" to "res"                         */

    "2:                      \n"

    : "=&b" (res), "=&b" (temp)

    : "b" (lock), "b" (old), "b" (set)

    : "cc", "memory");

    return res;

}

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    ngx_atomic_uint_t  res, temp;

    __asm__ volatile (

    "    lwsync              \n" /* write barrier                            */

    "1:  ldarx   %0, 0, %2   \n" /* load from [value] into "res"             */

                                 /*   and store reservation                  */

    "    add     %1, %0, %3  \n" /* "res" + "add" store in "temp"            */

    "    stdcx.  %1, 0, %2   \n" /* store "temp" into [value] if reservation */

                                 /*   is not cleared                         */

    "    bne-    1b          \n" /* try again if reservation was cleared     */

    "    isync               \n" /* read barrier                             */

    : "=&b" (res), "=&b" (temp)

    : "b" (value), "b" (add)

    : "cc", "memory");

    return res;

}

#if (NGX_SMP)

‌#define ngx_memory_barrier()                                                  \

    __asm__ volatile ("isync  \n  lwsync  \n" ::: "memory")

#else

‌#define ngx_memory_barrier()   __asm__ volatile ("" ::: "memory")

#endif

#else

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    ngx_atomic_uint_t  res, temp;

    __asm__ volatile (

    "    li      %0, 0       \n" /* preset "0" to "res"                      */

    "    eieio               \n" /* write barrier                            */

    "1:                      \n"

    "    lwarx   %1, 0, %2   \n" /* load from [lock] into "temp"             */

                                 /*   and store reservation                  */

    "    cmpw    %1, %3      \n" /* compare "temp" and "old"                 */

    "    bne-    2f          \n" /* not equal                                */

    "    stwcx.  %4, 0, %2   \n" /* store "set" into [lock] if reservation   */

                                 /*   is not cleared                         */

    "    bne-    1b          \n" /* the reservation was cleared              */

    "    isync               \n" /* read barrier                             */

    "    li      %0, 1       \n" /* set "1" to "res"                         */

    "2:                      \n"

    : "=&b" (res), "=&b" (temp)

    : "b" (lock), "b" (old), "b" (set)

    : "cc", "memory");

    return res;

}

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    ngx_atomic_uint_t  res, temp;

    __asm__ volatile (

    "    eieio               \n" /* write barrier                            */

    "1:  lwarx   %0, 0, %2   \n" /* load from [value] into "res"             */

                                 /*   and store reservation                  */

    "    add     %1, %0, %3  \n" /* "res" + "add" store in "temp"            */

    "    stwcx.  %1, 0, %2   \n" /* store "temp" into [value] if reservation */

                                 /*   is not cleared                         */

    "    bne-    1b          \n" /* try again if reservation was cleared     */

    "    isync               \n" /* read barrier                             */

    : "=&b" (res), "=&b" (temp)

    : "b" (value), "b" (add)

    : "cc", "memory");

    return res;

}

#if (NGX_SMP)

‌#define ngx_memory_barrier()                                                  \

    __asm__ volatile ("isync  \n  eieio  \n" ::: "memory")

#else

‌#define ngx_memory_barrier()   __asm__ volatile ("" ::: "memory")

#endif

#endif

‌#define ngx_cpu_pause()

src/os/unix/ngx_linux.h - nginx-1.7.9

Macros defined

_NGX_LINUX_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_LINUX_H_INCLUDED_

‌#define _NGX_LINUX_H_INCLUDED_

ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

extern int ngx_linux_rtsig_max;

#endif /* _NGX_LINUX_H_INCLUDED_ */

src/os/unix/ngx_setproctitle.c - nginx-1.7.9

Global variables defined

ngx_os_argv_last

Functions defined

ngx_init_setproctitle

ngx_setproctitle

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

#if (NGX_SETPROCTITLE_USES_ENV)

/*

* To change the process title in Linux and Solaris we have to set argv[1]

* to NULL and to copy the title to the same place where the argv[0] points to.

* However, argv[0] may be too small to hold a new title.  Fortunately, Linux

* and Solaris store argv[] and environ[] one after another.  So we should

* ensure that is the continuous memory and then we allocate the new memory

* for environ[] and copy it.  After this we could use the memory starting

* from argv[0] for our process title.

*

* The Solaris's standard /bin/ps does not show the changed process title.

* You have to use "/usr/ucb/ps -w" instead.  Besides, the UCB ps does not

* show a new title if its length less than the origin command line length.

* To avoid it we append to a new title the origin command line in the

* parenthesis.

*/

extern char **environ;

‌static char *ngx_os_argv_last;

ngx_int_t

‌ngx_init_setproctitle(ngx_log_t *log)

{

    u_char      *p;

    size_t       size;

    ngx_uint_t   i;

    size = 0;

    for (i = 0; environ[i]; i++) {

        size += ngx_strlen(environ[i]) + 1;

    }

    p = ngx_alloc(size, log);

    if (p == NULL) {

        return NGX_ERROR;

    }

    ngx_os_argv_last = ngx_os_argv[0];

    for (i = 0; ngx_os_argv[i]; i++) {

        if (ngx_os_argv_last == ngx_os_argv[i]) {

            ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1;

        }

    }

    for (i = 0; environ[i]; i++) {

        if (ngx_os_argv_last == environ[i]) {

            size = ngx_strlen(environ[i]) + 1;

            ngx_os_argv_last = environ[i] + size;

            ngx_cpystrn(p, (u_char *) environ[i], size);

            environ[i] = (char *) p;

            p += size;

        }

    }

    ngx_os_argv_last--;

    return NGX_OK;

}

void

‌ngx_setproctitle(char *title)

{

    u_char     *p;

#if (NGX_SOLARIS)

    ngx_int_t   i;

    size_t      size;

#endif

    ngx_os_argv[1] = NULL;

    p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) "nginx: ",

                    ngx_os_argv_last - ngx_os_argv[0]);

    p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p);

#if (NGX_SOLARIS)

    size = 0;

    for (i = 0; i < ngx_argc; i++) {

        size += ngx_strlen(ngx_argv[i]) + 1;

    }

    if (size > (size_t) ((char *) p - ngx_os_argv[0])) {

        /*

         * ngx_setproctitle() is too rare operation so we use

         * the non-optimized copies

         */

        p = ngx_cpystrn(p, (u_char *) " (", ngx_os_argv_last - (char *) p);

        for (i = 0; i < ngx_argc; i++) {

            p = ngx_cpystrn(p, (u_char *) ngx_argv[i],

                            ngx_os_argv_last - (char *) p);

            p = ngx_cpystrn(p, (u_char *) " ", ngx_os_argv_last - (char *) p);

        }

        if (*(p - 1) == ' ') {

            *(p - 1) = ')';

        }

    }

#endif

    if (ngx_os_argv_last - (char *) p) {

        ngx_memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p);

    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,

                   "setproctitle: \"%s\"", ngx_os_argv[0]);

}

#endif /* NGX_SETPROCTITLE_USES_ENV */

src/os/unix/ngx_solaris.h - nginx-1.7.9

Macros defined

_NGX_SOLARIS_H_INCLUDED_

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#ifndef _NGX_SOLARIS_H_INCLUDED_

‌#define _NGX_SOLARIS_H_INCLUDED_

ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,

    off_t limit);

#endif /* _NGX_SOLARIS_H_INCLUDED_ */

src/os/unix/ngx_solaris_init.c - nginx-1.7.9

Global variables defined

ngx_solaris_io

ngx_solaris_release

ngx_solaris_sysname

ngx_solaris_version

Functions defined

ngx_os_specific_init

ngx_os_specific_status

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <ngx_config.h>

#include <ngx_core.h>

‌char ngx_solaris_sysname[20];

‌char ngx_solaris_release[10];

‌char ngx_solaris_version[50];

‌static ngx_os_io_t ngx_solaris_io = {

    ngx_unix_recv,

    ngx_readv_chain,

    ngx_udp_unix_recv,

    ngx_unix_send,

#if (NGX_HAVE_SENDFILE)

    ngx_solaris_sendfilev_chain,

    NGX_IO_SENDFILE

#else

    ngx_writev_chain,

    0

#endif

};

ngx_int_t

‌ngx_os_specific_init(ngx_log_t *log)

{

    if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname))

        == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysinfo(SI_SYSNAME) failed");

        return NGX_ERROR;

    }

    if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release))

        == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysinfo(SI_RELEASE) failed");

        return NGX_ERROR;

    }

    if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version))

        == -1)

    {

        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,

                      "sysinfo(SI_SYSNAME) failed");

        return NGX_ERROR;

    }

    ngx_os_io = ngx_solaris_io;

    return NGX_OK;

}

void

‌ngx_os_specific_status(ngx_log_t *log)

{

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",

                  ngx_solaris_sysname, ngx_solaris_release);

    ngx_log_error(NGX_LOG_NOTICE, log, 0, "version: %s",

                  ngx_solaris_version);

}

src/os/unix/ngx_sunpro_atomic_sparc64.h - nginx-1.7.9

Functions defined

ngx_atomic_cmp_set

ngx_atomic_fetch_add

Macros defined

NGX_CASA

NGX_CASA

ngx_cpu_pause

ngx_memory_barrier

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#if (NGX_PTR_SIZE == 4)

‌#define NGX_CASA  ngx_casa

#else

‌#define NGX_CASA  ngx_casxa

#endif

ngx_atomic_uint_t

ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);

ngx_atomic_uint_t

ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);

/* the code in src/os/unix/ngx_sunpro_sparc64.il */

static ngx_inline ngx_atomic_uint_t

‌ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

    ngx_atomic_uint_t set)

{

    set = NGX_CASA(set, old, lock);

    return (set == old);

}

static ngx_inline ngx_atomic_int_t

‌ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)

{

    ngx_atomic_uint_t  old, res;

    old = *value;

    for ( ;; ) {

        res = old + add;

        res = NGX_CASA(res, old, value);

        if (res == old) {

            return res;

        }

        old = res;

    }

}

‌#define ngx_memory_barrier()                                                  \

        __asm (".volatile");                                                  \

        __asm ("membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad");   \

        __asm (".nonvolatile")

‌#define ngx_cpu_pause()

src/os/unix/rfork_thread.S - nginx-1.7.9

Macros defined

KERNCALL

Source code

/*

* Copyright (C) Igor Sysoev

* Copyright (C) Nginx, Inc.

*/

#include <sys/syscall.h>

#include <machine/asm.h>

/*

* rfork_thread(3) - rfork_thread(flags, stack, func, arg);

*/

‌#define    KERNCALL    int $0x80

ENTRY(rfork_thread)

    push    %ebp

    mov    %esp, %ebp

    push    %esi

    mov    12(%ebp), %esi    # the thread stack address

    sub    $4, %esi

    mov    20(%ebp), %eax    # the thread argument

    mov    %eax, (%esi)

    sub    $4, %esi

    mov    16(%ebp), %eax    # the thread start address

    mov    %eax, (%esi)

    push    8(%ebp)        # rfork(2) flags

    push    $0

    mov    $SYS_rfork, %eax

    KERNCALL

    jc    error

    cmp    $0, %edx

    jne    child

parent:

    add    $8, %esp

    pop    %esi

    leave

    ret

child:

    mov    %esi, %esp

    pop    %eax

    call    *%eax        # call a thread start address ...

    add    $4, %esp

    push    %eax

    push    $0

    mov    $SYS_exit, %eax    # ... and exit(2) after a thread would return

    KERNCALL

error:

    add    $8, %esp

    pop    %esi

    leave

    PIC_PROLOGUE

    /* libc's cerror: jmp  PIC_PLT(HIDENAME(cerror)) */

    push    %eax

    call    PIC_PLT(CNAME(__error))

    pop    %ecx

    PIC_EPILOGUE

    mov    %ecx, (%eax)

    mov    $-1, %eax

    mov    $-1, %edx

    ret