|  |  | 
|  | /* | 
|  | * Copyright (C) Igor Sysoev | 
|  | * Copyright (C) Nginx, Inc. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <ngx_config.h> | 
|  | #include <ngx_core.h> | 
|  |  | 
|  |  | 
|  | typedef struct { | 
|  | ngx_flag_t   pcre_jit; | 
|  | ngx_list_t  *studies; | 
|  | } ngx_regex_conf_t; | 
|  |  | 
|  |  | 
|  | static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool); | 
|  | static ngx_inline void ngx_regex_malloc_done(void); | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  | static void * ngx_libc_cdecl ngx_regex_malloc(size_t size, void *data); | 
|  | static void ngx_libc_cdecl ngx_regex_free(void *p, void *data); | 
|  | #else | 
|  | static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); | 
|  | static void ngx_libc_cdecl ngx_regex_free(void *p); | 
|  | #endif | 
|  | static void ngx_regex_cleanup(void *data); | 
|  |  | 
|  | 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_FLAG, | 
|  | 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_regex_pool; | 
|  | static ngx_list_t             *ngx_regex_studies; | 
|  | static ngx_uint_t              ngx_regex_direct_alloc; | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  | static pcre2_compile_context  *ngx_regex_compile_context; | 
|  | static pcre2_match_data       *ngx_regex_match_data; | 
|  | static ngx_uint_t              ngx_regex_match_data_size; | 
|  | #endif | 
|  |  | 
|  |  | 
|  | void | 
|  | ngx_regex_init(void) | 
|  | { | 
|  | #if !(NGX_PCRE2) | 
|  | pcre_malloc = ngx_regex_malloc; | 
|  | pcre_free = ngx_regex_free; | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_inline void | 
|  | ngx_regex_malloc_init(ngx_pool_t *pool) | 
|  | { | 
|  | ngx_regex_pool = pool; | 
|  | ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_inline void | 
|  | ngx_regex_malloc_done(void) | 
|  | { | 
|  | ngx_regex_pool = NULL; | 
|  | ngx_regex_direct_alloc = 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_regex_compile(ngx_regex_compile_t *rc) | 
|  | { | 
|  | int                     n, errcode; | 
|  | char                   *p; | 
|  | u_char                  errstr[128]; | 
|  | size_t                  erroff; | 
|  | uint32_t                options; | 
|  | pcre2_code             *re; | 
|  | ngx_regex_elt_t        *elt; | 
|  | pcre2_general_context  *gctx; | 
|  | pcre2_compile_context  *cctx; | 
|  |  | 
|  | if (ngx_regex_compile_context == NULL) { | 
|  | /* | 
|  | * Allocate a compile context if not yet allocated.  This uses | 
|  | * direct allocations from heap, so the result can be cached | 
|  | * even at runtime. | 
|  | */ | 
|  |  | 
|  | ngx_regex_malloc_init(NULL); | 
|  |  | 
|  | gctx = pcre2_general_context_create(ngx_regex_malloc, ngx_regex_free, | 
|  | NULL); | 
|  | if (gctx == NULL) { | 
|  | ngx_regex_malloc_done(); | 
|  | goto nomem; | 
|  | } | 
|  |  | 
|  | cctx = pcre2_compile_context_create(gctx); | 
|  | if (cctx == NULL) { | 
|  | pcre2_general_context_free(gctx); | 
|  | ngx_regex_malloc_done(); | 
|  | goto nomem; | 
|  | } | 
|  |  | 
|  | ngx_regex_compile_context = cctx; | 
|  |  | 
|  | pcre2_general_context_free(gctx); | 
|  | ngx_regex_malloc_done(); | 
|  | } | 
|  |  | 
|  | options = 0; | 
|  |  | 
|  | if (rc->options & NGX_REGEX_CASELESS) { | 
|  | options |= PCRE2_CASELESS; | 
|  | } | 
|  |  | 
|  | if (rc->options & NGX_REGEX_MULTILINE) { | 
|  | options |= PCRE2_MULTILINE; | 
|  | } | 
|  |  | 
|  | if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) { | 
|  | rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
|  | "regex \"%V\" compilation failed: invalid options", | 
|  | &rc->pattern) | 
|  | - rc->err.data; | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | ngx_regex_malloc_init(rc->pool); | 
|  |  | 
|  | re = pcre2_compile(rc->pattern.data, rc->pattern.len, options, | 
|  | &errcode, &erroff, ngx_regex_compile_context); | 
|  |  | 
|  | /* ensure that there is no current pool */ | 
|  | ngx_regex_malloc_done(); | 
|  |  | 
|  | if (re == NULL) { | 
|  | pcre2_get_error_message(errcode, errstr, 128); | 
|  |  | 
|  | if ((size_t) erroff == rc->pattern.len) { | 
|  | rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
|  | "pcre2_compile() failed: %s in \"%V\"", | 
|  | errstr, &rc->pattern) | 
|  | - rc->err.data; | 
|  |  | 
|  | } else { | 
|  | rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
|  | "pcre2_compile() failed: %s in \"%V\" at \"%s\"", | 
|  | errstr, &rc->pattern, rc->pattern.data + erroff) | 
|  | - rc->err.data; | 
|  | } | 
|  |  | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | rc->regex = re; | 
|  |  | 
|  | /* do not study at runtime */ | 
|  |  | 
|  | if (ngx_regex_studies != NULL) { | 
|  | elt = ngx_list_push(ngx_regex_studies); | 
|  | if (elt == NULL) { | 
|  | goto nomem; | 
|  | } | 
|  |  | 
|  | elt->regex = rc->regex; | 
|  | elt->name = rc->pattern.data; | 
|  | } | 
|  |  | 
|  | n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures); | 
|  | if (n < 0) { | 
|  | p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_CAPTURECOUNT) failed: %d"; | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | if (rc->captures == 0) { | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | n = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &rc->named_captures); | 
|  | if (n < 0) { | 
|  | p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMECOUNT) failed: %d"; | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | if (rc->named_captures == 0) { | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | n = pcre2_pattern_info(re, PCRE2_INFO_NAMEENTRYSIZE, &rc->name_size); | 
|  | if (n < 0) { | 
|  | p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMEENTRYSIZE) failed: %d"; | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | n = pcre2_pattern_info(re, PCRE2_INFO_NAMETABLE, &rc->names); | 
|  | if (n < 0) { | 
|  | p = "pcre2_pattern_info(\"%V\", PCRE2_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; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_regex_compile(ngx_regex_compile_t *rc) | 
|  | { | 
|  | int               n, erroff; | 
|  | char             *p; | 
|  | pcre             *re; | 
|  | const char       *errstr; | 
|  | ngx_uint_t        options; | 
|  | ngx_regex_elt_t  *elt; | 
|  |  | 
|  | options = 0; | 
|  |  | 
|  | if (rc->options & NGX_REGEX_CASELESS) { | 
|  | options |= PCRE_CASELESS; | 
|  | } | 
|  |  | 
|  | if (rc->options & NGX_REGEX_MULTILINE) { | 
|  | options |= PCRE_MULTILINE; | 
|  | } | 
|  |  | 
|  | if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) { | 
|  | rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, | 
|  | "regex \"%V\" compilation failed: invalid options", | 
|  | &rc->pattern) | 
|  | - rc->err.data; | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | ngx_regex_malloc_init(rc->pool); | 
|  |  | 
|  | re = pcre_compile((const char *) rc->pattern.data, (int) 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_regex_studies != NULL) { | 
|  | elt = ngx_list_push(ngx_regex_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; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size) | 
|  | { | 
|  | size_t      *ov; | 
|  | ngx_int_t    rc; | 
|  | ngx_uint_t   n, i; | 
|  |  | 
|  | /* | 
|  | * The pcre2_match() function might allocate memory for backtracking | 
|  | * frames, typical allocations are from 40k and above.  So the allocator | 
|  | * is configured to do direct allocations from heap during matching. | 
|  | */ | 
|  |  | 
|  | ngx_regex_malloc_init(NULL); | 
|  |  | 
|  | if (ngx_regex_match_data == NULL | 
|  | || size > ngx_regex_match_data_size) | 
|  | { | 
|  | /* | 
|  | * Allocate a match data if not yet allocated or smaller than | 
|  | * needed. | 
|  | */ | 
|  |  | 
|  | if (ngx_regex_match_data) { | 
|  | pcre2_match_data_free(ngx_regex_match_data); | 
|  | } | 
|  |  | 
|  | ngx_regex_match_data_size = size; | 
|  | ngx_regex_match_data = pcre2_match_data_create(size / 3, NULL); | 
|  |  | 
|  | if (ngx_regex_match_data == NULL) { | 
|  | rc = PCRE2_ERROR_NOMEMORY; | 
|  | goto failed; | 
|  | } | 
|  | } | 
|  |  | 
|  | rc = pcre2_match(re, s->data, s->len, 0, 0, ngx_regex_match_data, NULL); | 
|  |  | 
|  | if (rc < 0) { | 
|  | goto failed; | 
|  | } | 
|  |  | 
|  | n = pcre2_get_ovector_count(ngx_regex_match_data); | 
|  | ov = pcre2_get_ovector_pointer(ngx_regex_match_data); | 
|  |  | 
|  | if (n > size / 3) { | 
|  | n = size / 3; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < n; i++) { | 
|  | captures[i * 2] = ov[i * 2]; | 
|  | captures[i * 2 + 1] = ov[i * 2 + 1]; | 
|  | } | 
|  |  | 
|  | failed: | 
|  |  | 
|  | ngx_regex_malloc_done(); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size) | 
|  | { | 
|  | return pcre_exec(re->code, re->extra, (const char *) s->data, s->len, | 
|  | 0, 0, captures, size); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  |  | 
|  | static void * ngx_libc_cdecl | 
|  | ngx_regex_malloc(size_t size, void *data) | 
|  | { | 
|  | if (ngx_regex_pool) { | 
|  | return ngx_palloc(ngx_regex_pool, size); | 
|  | } | 
|  |  | 
|  | if (ngx_regex_direct_alloc) { | 
|  | return ngx_alloc(size, ngx_cycle->log); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void ngx_libc_cdecl | 
|  | ngx_regex_free(void *p, void *data) | 
|  | { | 
|  | if (ngx_regex_direct_alloc) { | 
|  | ngx_free(p); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | static void * ngx_libc_cdecl | 
|  | ngx_regex_malloc(size_t size) | 
|  | { | 
|  | if (ngx_regex_pool) { | 
|  | return ngx_palloc(ngx_regex_pool, size); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void ngx_libc_cdecl | 
|  | ngx_regex_free(void *p) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | static void | 
|  | ngx_regex_cleanup(void *data) | 
|  | { | 
|  | #if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT) | 
|  | ngx_regex_conf_t *rcf = data; | 
|  |  | 
|  | ngx_uint_t        i; | 
|  | ngx_list_part_t  *part; | 
|  | ngx_regex_elt_t  *elts; | 
|  |  | 
|  | part = &rcf->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; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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.  In PCRE2, we call the pcre2_code_free() function | 
|  | * for the same reason. | 
|  | */ | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  | pcre2_code_free(elts[i].regex); | 
|  | #else | 
|  | if (elts[i].regex->extra != NULL) { | 
|  | pcre_free_study(elts[i].regex->extra); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * On configuration parsing errors ngx_regex_module_init() will not | 
|  | * be called.  Make sure ngx_regex_studies is properly cleared anyway. | 
|  | */ | 
|  |  | 
|  | ngx_regex_studies = NULL; | 
|  |  | 
|  | #if (NGX_PCRE2) | 
|  |  | 
|  | /* | 
|  | * Free compile context and match data.  If needed at runtime by | 
|  | * the new cycle, these will be re-allocated. | 
|  | */ | 
|  |  | 
|  | if (ngx_regex_compile_context) { | 
|  | pcre2_compile_context_free(ngx_regex_compile_context); | 
|  | ngx_regex_compile_context = NULL; | 
|  | } | 
|  |  | 
|  | if (ngx_regex_match_data) { | 
|  | pcre2_match_data_free(ngx_regex_match_data); | 
|  | ngx_regex_match_data = NULL; | 
|  | ngx_regex_match_data_size = 0; | 
|  | } | 
|  |  | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_regex_module_init(ngx_cycle_t *cycle) | 
|  | { | 
|  | int                opt; | 
|  | #if !(NGX_PCRE2) | 
|  | const char        *errstr; | 
|  | #endif | 
|  | ngx_uint_t         i; | 
|  | ngx_list_part_t   *part; | 
|  | ngx_regex_elt_t   *elts; | 
|  | ngx_regex_conf_t  *rcf; | 
|  |  | 
|  | opt = 0; | 
|  |  | 
|  | rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); | 
|  |  | 
|  | #if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT) | 
|  |  | 
|  | if (rcf->pcre_jit) { | 
|  | #if (NGX_PCRE2) | 
|  | opt = 1; | 
|  | #else | 
|  | opt = PCRE_STUDY_JIT_COMPILE; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | ngx_regex_malloc_init(cycle->pool); | 
|  |  | 
|  | part = &rcf->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 (NGX_PCRE2) | 
|  |  | 
|  | if (opt) { | 
|  | int  n; | 
|  |  | 
|  | n = pcre2_jit_compile(elts[i].regex, PCRE2_JIT_COMPLETE); | 
|  |  | 
|  | if (n != 0) { | 
|  | ngx_log_error(NGX_LOG_INFO, cycle->log, 0, | 
|  | "pcre2_jit_compile() failed: %d in \"%s\", " | 
|  | "ignored", | 
|  | n, elts[i].name); | 
|  | } | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | 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 | 
|  | #endif | 
|  | } | 
|  |  | 
|  | ngx_regex_malloc_done(); | 
|  |  | 
|  | ngx_regex_studies = NULL; | 
|  | #if (NGX_PCRE2) | 
|  | ngx_regex_compile_context = NULL; | 
|  | #endif | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void * | 
|  | ngx_regex_create_conf(ngx_cycle_t *cycle) | 
|  | { | 
|  | ngx_regex_conf_t    *rcf; | 
|  | ngx_pool_cleanup_t  *cln; | 
|  |  | 
|  | rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); | 
|  | if (rcf == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | rcf->pcre_jit = NGX_CONF_UNSET; | 
|  |  | 
|  | cln = ngx_pool_cleanup_add(cycle->pool, 0); | 
|  | if (cln == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | cln->handler = ngx_regex_cleanup; | 
|  | cln->data = rcf; | 
|  |  | 
|  | rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); | 
|  | if (rcf->studies == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | ngx_regex_studies = rcf->studies; | 
|  |  | 
|  | 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_PCRE2) | 
|  | { | 
|  | int       r; | 
|  | uint32_t  jit; | 
|  |  | 
|  | jit = 0; | 
|  | r = pcre2_config(PCRE2_CONFIG_JIT, &jit); | 
|  |  | 
|  | if (r != 0 || jit != 1) { | 
|  | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 
|  | "PCRE2 library does not support JIT"); | 
|  | *fp = 0; | 
|  | } | 
|  | } | 
|  | #elif (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; | 
|  | } |