├── README ├── config ├── ngx_http_user_agent_module.c ├── ngx_trie.c └── ngx_trie.h /README: -------------------------------------------------------------------------------- 1 | # HTTP user_agent module for Tengine 2 | #syntax: 3 | 4 | user_agent $variable_name { 5 | greedy name; 6 | 7 | name [([+|-]version) | (version1~version2)] value; 8 | } 9 | 10 | if ($variable == value) { 11 | echo hello; 12 | } 13 | 14 | 15 | ## Introduction 16 | 17 | greedy: 18 | We specify the keyword in the user_agent string from right to left, and this is more efficient. As usual, we use the greedy algorithm. It will return immediately after the keyword being found. 19 | 20 | E.g 1. "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)", this string is MSIE's user_agent string, we will return when we find the keyword "MSIE". But the truth is not alway like this: 21 | E.g 2. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", This is Chrome's user_agent. We will match Safari frist. If we define safari is greedy, it scans the string in a reverse order. If a keyword is greedy, it will not return when it matches the keyword at the first time. It will continue to scan the string. 22 | 23 | default: 24 | set the default value of this variable; 25 | 26 | The directive format is like this in the block: 27 | name version value; 28 | 29 | name: the name of operating_system, browser, crawler and so on; 30 | version: It can be omitted, and it support multiple formats; 31 | value: It is the value filled to the variable; 32 | 33 | for example: 34 | 35 | user_agent $example { 36 | #set default value 37 | default msie; 38 | 39 | #define safari is greedy 40 | greedy safari; 41 | 42 | #match exact version 43 | msie 6.0 1; 44 | 45 | #match interval 46 | msie 7.0~8.0 2; 47 | 48 | #match greater than version 9.0 49 | msie 9.0+ 3; 50 | 51 | #match less than version 4.0 (include 4.0) 52 | msie 4.0- 4; 53 | 54 | #match all 55 | Chrome 5; 56 | } 57 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_user_agent_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_user_agent_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ 4 | $ngx_addon_dir/ngx_trie.c 5 | $ngx_addon_dir/ngx_http_user_agent_module.c" 6 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ 7 | $ngx_addon_dir/ngx_trie.h" 8 | -------------------------------------------------------------------------------- /ngx_http_user_agent_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2010-2012 Alibaba Group Holding Limited 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "ngx_trie.h" 11 | 12 | 13 | #define NGX_HTTP_UA_MATCH_LE '-' 14 | #define NGX_HTTP_UA_MATCH_GE '+' 15 | #define NGX_HTTP_UA_MATCH_INTERVAL '~' 16 | #define NGX_HTTP_UA_MATCH_EXACT '=' 17 | 18 | #define NGX_HTTP_UA_MAX_OFFSET 8 19 | 20 | #define NGX_HTTP_UA_MAX_VERSION_VALUE 99999999999999999ULL 21 | #define NGX_HTTP_UA_MIN_VERSION_VALUE 0ULL 22 | #define NGX_HTTP_UA_MAX_INT64 1000000000000ULL 23 | 24 | 25 | typedef struct { 26 | uint64_t left; 27 | uint64_t right; 28 | 29 | ngx_http_variable_value_t *var; 30 | } ngx_http_user_agent_interval_t; 31 | 32 | 33 | typedef struct { 34 | ngx_trie_t *trie; 35 | ngx_http_variable_value_t *default_value; 36 | ngx_pool_t *pool; 37 | } ngx_http_user_agent_ctx_t; 38 | 39 | 40 | static char *ngx_http_user_agent_block(ngx_conf_t *cf, ngx_command_t *cmd, 41 | void *conf); 42 | static char *ngx_http_user_agent(ngx_conf_t *cf, ngx_command_t *cmd, 43 | void *conf); 44 | static ngx_int_t ngx_http_user_agent_variable(ngx_http_request_t *r, 45 | ngx_http_variable_value_t *v, uintptr_t data); 46 | static ngx_http_user_agent_interval_t *ngx_http_user_agent_get_version( 47 | ngx_conf_t *cf, ngx_str_t *value); 48 | 49 | 50 | static ngx_command_t ngx_http_user_agent_commands[] = { 51 | 52 | { ngx_string("user_agent"), 53 | NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, 54 | ngx_http_user_agent_block, 55 | NGX_HTTP_MAIN_CONF_OFFSET, 56 | 0, 57 | NULL }, 58 | 59 | ngx_null_command 60 | }; 61 | 62 | 63 | static ngx_http_module_t ngx_http_user_agent_module_ctx = { 64 | NULL, /* preconfiguration */ 65 | NULL, /* postconfiguration */ 66 | 67 | NULL, /* create main configuration */ 68 | NULL, /* init main configuration */ 69 | 70 | NULL, /* create server configuration */ 71 | NULL, /* merge server configuration */ 72 | 73 | NULL, /* create location configuration */ 74 | NULL /* merge lcoation configuration */ 75 | }; 76 | 77 | 78 | ngx_module_t ngx_http_user_agent_module = { 79 | NGX_MODULE_V1, 80 | &ngx_http_user_agent_module_ctx, /* module context */ 81 | ngx_http_user_agent_commands, /* module directives */ 82 | NGX_HTTP_MODULE, /* module type */ 83 | NULL, /* init master */ 84 | NULL, /* init module */ 85 | NULL, /* init process */ 86 | NULL, /* init thread */ 87 | NULL, /* exit thread */ 88 | NULL, /* exit process */ 89 | NULL, /* exit master */ 90 | NGX_MODULE_V1_PADDING 91 | }; 92 | 93 | 94 | static char * 95 | ngx_http_user_agent_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 96 | { 97 | char *rv; 98 | ngx_str_t *value, name; 99 | ngx_conf_t save; 100 | ngx_http_variable_t *var; 101 | ngx_http_user_agent_ctx_t *ctx; 102 | 103 | value = cf->args->elts; 104 | 105 | name = value[1]; 106 | name.data++; 107 | name.len--; 108 | 109 | var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); 110 | if (var == NULL) { 111 | return NGX_CONF_ERROR; 112 | } 113 | 114 | ctx = ngx_palloc(cf->pool, sizeof(ngx_http_user_agent_ctx_t)); 115 | if (ctx == NULL) { 116 | return NGX_CONF_ERROR; 117 | } 118 | 119 | ctx->pool = cf->pool; 120 | ctx->trie = ngx_trie_create(ctx->pool); 121 | if (ctx->trie == NULL) { 122 | return NGX_CONF_ERROR; 123 | } 124 | 125 | ctx->default_value = NULL; 126 | 127 | var->get_handler = ngx_http_user_agent_variable; 128 | var->data = (uintptr_t) ctx; 129 | 130 | save = *cf; 131 | cf->ctx = ctx; 132 | cf->handler = ngx_http_user_agent; 133 | cf->handler_conf = conf; 134 | 135 | rv = ngx_conf_parse(cf, NULL); 136 | if (NGX_OK != ctx->trie->build_clue(ctx->trie)) { 137 | return NGX_CONF_ERROR; 138 | } 139 | 140 | *cf = save; 141 | if (ctx->default_value == NULL) { 142 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no default value"); 143 | rv = NGX_CONF_ERROR; 144 | } 145 | 146 | return rv; 147 | } 148 | 149 | 150 | static ngx_http_user_agent_interval_t * 151 | ngx_http_user_agent_get_version(ngx_conf_t *cf, ngx_str_t *value) 152 | { 153 | char op; 154 | uint64_t ver, scale, version; 155 | ngx_uint_t i, n; 156 | ngx_http_user_agent_interval_t *interval; 157 | 158 | op = NGX_HTTP_UA_MATCH_EXACT; 159 | scale = NGX_HTTP_UA_MAX_INT64; 160 | version = 0; 161 | ver = 0; 162 | n = 0; 163 | 164 | interval = ngx_palloc(cf->pool, sizeof(ngx_http_user_agent_interval_t)); 165 | if(interval == NULL) { 166 | return NULL; 167 | } 168 | 169 | interval->var = ngx_pcalloc(cf->pool, sizeof(ngx_http_variable_value_t)); 170 | if (interval->var == NULL) { 171 | return NULL; 172 | } 173 | 174 | interval->left = NGX_HTTP_UA_MIN_VERSION_VALUE; 175 | interval->right = NGX_HTTP_UA_MAX_VERSION_VALUE; 176 | 177 | for (i = 0; i < value->len; i++) { 178 | if (value->data[i] >= '0' && value->data[i] <= '9') { 179 | ver = ver * 10 + value->data[i] - '0'; 180 | continue; 181 | } 182 | 183 | if (value->data[i] == '.') { 184 | version += ver * scale; 185 | ver = 0; 186 | scale /= 10000; 187 | 188 | } else if (value->data[i] == NGX_HTTP_UA_MATCH_LE) { 189 | if (i != value->len - 1) { 190 | goto error; 191 | } 192 | 193 | op = NGX_HTTP_UA_MATCH_LE; 194 | } else if (value->data[i] == NGX_HTTP_UA_MATCH_EXACT) { 195 | if (i != value->len - 1) { 196 | goto error; 197 | } 198 | } else if (value->data[i] == NGX_HTTP_UA_MATCH_GE) { 199 | if (i != value->len - 1) { 200 | goto error; 201 | } 202 | 203 | op = NGX_HTTP_UA_MATCH_GE; 204 | } else if (value->data[i] == NGX_HTTP_UA_MATCH_INTERVAL) { 205 | op = NGX_HTTP_UA_MATCH_INTERVAL; 206 | if (n >= 2) { 207 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too many versions"); 208 | return NULL; 209 | } 210 | 211 | version += ver * scale; 212 | interval->left = version; 213 | n++; 214 | 215 | ver = 0; 216 | scale = NGX_HTTP_UA_MAX_INT64; 217 | version = 0; 218 | 219 | if (i + 1 >= value->len) { 220 | goto error; 221 | } 222 | 223 | if (!(value->data[i + 1] >= '0'&&value->data[i + 1] <= '9')) { 224 | goto error; 225 | } 226 | } else { 227 | goto error; 228 | } 229 | } 230 | 231 | version += ver * scale; 232 | if (op == NGX_HTTP_UA_MATCH_LE || op == NGX_HTTP_UA_MATCH_INTERVAL) { 233 | interval->right = version; 234 | 235 | } else if (op == NGX_HTTP_UA_MATCH_GE) { 236 | interval->left = version; 237 | 238 | } else if (op == NGX_HTTP_UA_MATCH_EXACT) { 239 | interval->left = version; 240 | interval->right = version; 241 | } 242 | 243 | return interval; 244 | 245 | error: 246 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid version"); 247 | return NULL; 248 | } 249 | 250 | 251 | static char * 252 | ngx_http_user_agent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 253 | { 254 | ngx_str_t *args, *name, file; 255 | ngx_uint_t i, nelts, mode; 256 | ngx_trie_t *trie; 257 | ngx_array_t *value; 258 | ngx_trie_node_t *node; 259 | ngx_http_user_agent_ctx_t *ctx; 260 | ngx_http_user_agent_interval_t *interval, *p; 261 | 262 | ctx = cf->ctx; 263 | trie = ctx->trie; 264 | 265 | args = cf->args->elts; 266 | nelts = cf->args->nelts; 267 | 268 | name = NULL; 269 | 270 | if (nelts <= 1) { 271 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 272 | "invalid first parameter"); 273 | return NGX_CONF_ERROR; 274 | 275 | } 276 | 277 | if (nelts == 2) { 278 | if (ngx_strcmp(args[0].data, "include") == 0) { 279 | 280 | file = args[1]; 281 | if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { 282 | return NGX_CONF_ERROR; 283 | } 284 | 285 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", 286 | file.data); 287 | return ngx_conf_parse(cf, &file); 288 | } 289 | 290 | if (ngx_strcmp(args[0].data, "default") == 0) { 291 | 292 | if (ctx->default_value != NULL) { 293 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "is duplicate"); 294 | return NGX_CONF_ERROR; 295 | } 296 | 297 | ctx->default_value = ngx_pcalloc(ctx->pool, 298 | sizeof(ngx_http_variable_t)); 299 | if (ctx->default_value == NULL) { 300 | return NGX_CONF_ERROR; 301 | } 302 | 303 | ctx->default_value->len = args[1].len; 304 | ctx->default_value->data = args[1].data; 305 | 306 | ctx->default_value->not_found = 0; 307 | ctx->default_value->no_cacheable =0; 308 | ctx->default_value->valid =1; 309 | 310 | return NGX_CONF_OK; 311 | } 312 | 313 | if (ngx_strcmp(args[0].data, "greedy") == 0) { 314 | mode = NGX_TRIE_REVERSE | NGX_TRIE_CONTINUE; 315 | trie->insert(trie, args + 1, mode); 316 | 317 | return NGX_CONF_OK; 318 | } 319 | } 320 | 321 | if (nelts == 2) { 322 | 323 | name = args; 324 | 325 | interval = ngx_pcalloc(ctx->pool, 326 | sizeof(ngx_http_user_agent_interval_t)); 327 | if (interval == NULL) { 328 | return NGX_CONF_ERROR; 329 | } 330 | 331 | interval->var = ngx_pcalloc(ctx->pool, 332 | sizeof(ngx_http_variable_value_t)); 333 | if (interval->var == NULL) { 334 | return NGX_CONF_ERROR; 335 | } 336 | 337 | interval->left = NGX_HTTP_UA_MIN_VERSION_VALUE; 338 | interval->right = NGX_HTTP_UA_MAX_VERSION_VALUE; 339 | 340 | interval->var->len = args[1].len; 341 | interval->var->data = args[1].data; 342 | 343 | interval->var->not_found = 0; 344 | interval->var->no_cacheable = 0; 345 | interval->var->valid = 1; 346 | 347 | goto insert; 348 | } 349 | 350 | if (nelts == 3) { 351 | 352 | name = args; 353 | interval = ngx_http_user_agent_get_version(cf, args + 1); 354 | if (interval == NULL) { 355 | return NGX_CONF_ERROR; 356 | } 357 | 358 | interval->var->len = args[2].len; 359 | interval->var->data = args[2].data; 360 | 361 | interval->var->not_found = 0; 362 | interval->var->no_cacheable =0; 363 | interval->var->valid = 1; 364 | 365 | goto insert; 366 | } 367 | 368 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too many args"); 369 | return NGX_CONF_ERROR; 370 | 371 | insert: 372 | 373 | mode = NGX_TRIE_REVERSE; 374 | node = trie->insert(trie, name, mode); 375 | if (node == NULL) { 376 | return NGX_CONF_ERROR; 377 | } 378 | 379 | value = (ngx_array_t *) node->value; 380 | if (value == NULL) { 381 | value = ngx_array_create(ctx->pool, 2, 382 | sizeof(ngx_http_user_agent_interval_t)); 383 | if (value == NULL) { 384 | return NGX_CONF_ERROR; 385 | } 386 | } 387 | 388 | p = (ngx_http_user_agent_interval_t *) value->elts; 389 | for (i = 0; i < value->nelts; i++) { 390 | if ((p[i].left >= interval->left && p[i].left <= interval->right) 391 | || (p[i].right >= interval->left && p[i].right <= interval->right) 392 | || (interval->left >= p[i].left && interval->left <= p[i].right) 393 | || (interval->right >= p[i].left && interval->right <= p[i].right)) 394 | { 395 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "interval covered"); 396 | return NGX_CONF_ERROR; 397 | } 398 | } 399 | 400 | p = (ngx_http_user_agent_interval_t *) ngx_array_push(value); 401 | if (p == NULL) { 402 | return NGX_CONF_ERROR; 403 | } 404 | 405 | *p = *interval; 406 | node->value = (void *) value; 407 | 408 | return NGX_CONF_OK; 409 | } 410 | 411 | 412 | static ngx_int_t 413 | ngx_http_user_agent_variable(ngx_http_request_t *r, 414 | ngx_http_variable_value_t *v, uintptr_t data) 415 | { 416 | uint64_t ver, scale, version; 417 | ngx_int_t i, n, pos, offset; 418 | ngx_str_t *user_agent; 419 | ngx_trie_t *trie; 420 | ngx_array_t *value; 421 | ngx_http_user_agent_ctx_t *uacf; 422 | ngx_http_user_agent_interval_t *array; 423 | 424 | uacf = (ngx_http_user_agent_ctx_t *) data; 425 | trie = uacf->trie; 426 | 427 | if (r->headers_in.user_agent == NULL) { 428 | goto end; 429 | } 430 | 431 | user_agent = &(r->headers_in.user_agent->value); 432 | 433 | value = trie->query(trie, user_agent, &pos, NGX_TRIE_REVERSE); 434 | if (value == NULL || pos < 0) { 435 | goto end; 436 | } 437 | 438 | version = 0; 439 | scale = NGX_HTTP_UA_MAX_INT64; 440 | ver = 0; 441 | offset = 0; 442 | 443 | for (/* void */; pos < (ngx_int_t) user_agent->len; pos++, offset++) { 444 | if (user_agent->data[pos] >= '0' 445 | && user_agent->data[pos] <= '9') { 446 | break; 447 | 448 | } else if (user_agent->data[pos] == ';' 449 | || user_agent->data[pos] == '(' 450 | || user_agent->data[pos] == ')') 451 | { 452 | break; 453 | } 454 | 455 | if(offset >= NGX_HTTP_UA_MAX_OFFSET) { 456 | break; 457 | } 458 | } 459 | 460 | array = value->elts; 461 | n = value->nelts; 462 | 463 | for (/* void */ ; pos < (ngx_int_t) user_agent->len; pos++) { 464 | 465 | if (user_agent->data[pos] == '.') { 466 | version += ver * scale; 467 | ver = 0; 468 | scale /= 10000; 469 | continue; 470 | 471 | } else if(user_agent->data[pos] >= '0' 472 | && user_agent->data[pos] <= '9') { 473 | 474 | ver = ver * 10 +user_agent->data[pos] - '0'; 475 | continue; 476 | } 477 | 478 | break; 479 | } 480 | 481 | version += ver * scale; 482 | for (i = 0; i < n; i++) { 483 | if (version >= array[i].left && version <= array[i].right) { 484 | *v = *(array[i].var); 485 | return NGX_OK; 486 | } 487 | } 488 | 489 | end: 490 | 491 | *v = *uacf->default_value; 492 | return NGX_OK; 493 | } 494 | -------------------------------------------------------------------------------- /ngx_trie.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2010-2012 Alibaba Group Holding Limited 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_trie.h" 10 | 11 | 12 | #define NGX_TRIE_MAX_QUEUE_SIZE 300 13 | #define NGX_TRIE_KIND 256 14 | 15 | 16 | ngx_trie_t * 17 | ngx_trie_create(ngx_pool_t *pool) 18 | { 19 | ngx_trie_t *trie; 20 | 21 | trie = ngx_palloc(pool, sizeof(ngx_trie_t)); 22 | if (trie == NULL) { 23 | return NULL; 24 | } 25 | 26 | trie->root = ngx_trie_node_create(pool); 27 | if (trie->root == NULL) { 28 | return NULL; 29 | } 30 | 31 | trie->pool = pool; 32 | trie->insert = ngx_trie_insert; 33 | trie->query = ngx_trie_query; 34 | trie->build_clue = ngx_trie_build_clue; 35 | 36 | return trie; 37 | } 38 | 39 | 40 | ngx_trie_node_t * 41 | ngx_trie_node_create(ngx_pool_t *pool) 42 | { 43 | ngx_trie_node_t *node; 44 | 45 | node = ngx_pcalloc(pool, sizeof(ngx_trie_node_t)); 46 | if (node == NULL) { 47 | return NULL; 48 | } 49 | 50 | return node; 51 | } 52 | 53 | 54 | ngx_trie_node_t * 55 | ngx_trie_insert(ngx_trie_t *trie, ngx_str_t *str, ngx_uint_t mode) 56 | { 57 | size_t i; 58 | ngx_int_t pos, step, index; 59 | ngx_trie_node_t *p, *root; 60 | 61 | root = trie->root; 62 | i = 0; 63 | 64 | if (mode & NGX_TRIE_REVERSE) { 65 | pos = str->len; 66 | step = -1; 67 | } else { 68 | pos = -1; 69 | step = 1; 70 | } 71 | 72 | p = root; 73 | 74 | while (i < str->len) { 75 | pos = pos + step; 76 | index = str->data[pos]; 77 | 78 | if (index < 0 || index >= NGX_TRIE_KIND) { 79 | continue; 80 | } 81 | 82 | if (p->next == NULL) { 83 | p->next = ngx_pcalloc(trie->pool, 84 | NGX_TRIE_KIND * sizeof(ngx_trie_node_t *)); 85 | 86 | if (p->next == NULL) { 87 | return NULL; 88 | } 89 | } 90 | 91 | if (p->next[index] == NULL) { 92 | p->next[index] = ngx_trie_node_create(trie->pool); 93 | } 94 | 95 | p = p->next[index]; 96 | i++; 97 | } 98 | 99 | p->key = str->len; 100 | if (mode & NGX_TRIE_CONTINUE) { 101 | p->greedy = 1; 102 | } 103 | 104 | return p; 105 | } 106 | 107 | 108 | ngx_int_t 109 | ngx_trie_build_clue(ngx_trie_t *trie) 110 | { 111 | ngx_int_t i, head, tail; 112 | ngx_trie_node_t *q[NGX_TRIE_MAX_QUEUE_SIZE], *p, *t, *root; 113 | 114 | head = tail = 0; 115 | root = trie->root; 116 | q[head++] = root; 117 | root->search_clue = NULL; 118 | 119 | while (head != tail) { 120 | t = q[tail++]; 121 | tail %= NGX_TRIE_MAX_QUEUE_SIZE; 122 | 123 | if (t->next == NULL) { 124 | continue; 125 | } 126 | 127 | p = NULL; 128 | 129 | for (i = 0; i< NGX_TRIE_KIND; i++) { 130 | if (t->next[i] == NULL) { 131 | continue; 132 | } 133 | 134 | if (t == root) { 135 | t->next[i]->search_clue = root; 136 | 137 | q[head++] = t->next[i]; 138 | head %= NGX_TRIE_MAX_QUEUE_SIZE; 139 | 140 | continue; 141 | } 142 | 143 | p = t->search_clue; 144 | 145 | while (p != NULL) { 146 | if (p->next !=NULL && p->next[i] != NULL) { 147 | t->next[i]->search_clue = p->next[i]; 148 | break; 149 | } 150 | p = p->search_clue; 151 | } 152 | 153 | if (p == NULL) { 154 | t->next[i]->search_clue = root; 155 | } 156 | 157 | q[head++] = t->next[i]; 158 | head %= NGX_TRIE_MAX_QUEUE_SIZE; 159 | } 160 | } 161 | 162 | return NGX_OK; 163 | } 164 | 165 | 166 | void * 167 | ngx_trie_query(ngx_trie_t *trie, ngx_str_t *str, ngx_int_t *version_pos, 168 | ngx_uint_t mode) 169 | { 170 | void *value; 171 | size_t i; 172 | ngx_int_t step, pos, index; 173 | ngx_trie_node_t *p, *root; 174 | 175 | value = NULL; 176 | root = trie->root; 177 | p = root; 178 | i = 0; 179 | 180 | if (mode & NGX_TRIE_REVERSE) { 181 | pos = str->len; 182 | step = -1; 183 | } else { 184 | pos = -1; 185 | step = 1; 186 | } 187 | 188 | if (p->next == NULL) { 189 | return NULL; 190 | } 191 | 192 | while (i < str->len) { 193 | pos += step; 194 | index = str->data[pos]; 195 | if (index < 0 || index >= NGX_TRIE_KIND) { 196 | index = 0; 197 | } 198 | 199 | while (p->next[index] == NULL) { 200 | if (p == root) { 201 | break; 202 | } 203 | p = p->search_clue; 204 | } 205 | 206 | p = p->next[index]; 207 | p = p == NULL ? root : p; 208 | if (p->key) { 209 | value = p->value; 210 | *version_pos = pos + p->key; 211 | if (!p->greedy) { 212 | return value; 213 | } 214 | p = root; 215 | } 216 | 217 | i++; 218 | } 219 | 220 | return value; 221 | } 222 | -------------------------------------------------------------------------------- /ngx_trie.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2010-2012 Alibaba Group Holding Limited 4 | */ 5 | 6 | 7 | #ifndef _NGX_TRIE_H_INCLUDE_ 8 | #define _NGX_TRIE_H_INCLUDE_ 9 | 10 | 11 | #include 12 | #include 13 | 14 | 15 | #define NGX_TRIE_REVERSE 1 16 | #define NGX_TRIE_CONTINUE 2 17 | 18 | 19 | typedef struct ngx_trie_s ngx_trie_t; 20 | typedef struct ngx_trie_node_s ngx_trie_node_t; 21 | 22 | typedef ngx_trie_node_t *(*ngx_trie_insert_pt)(ngx_trie_t *trie, 23 | ngx_str_t *str, ngx_uint_t mode); 24 | typedef ngx_int_t (*ngx_trie_build_clue_pt)(ngx_trie_t *trie); 25 | typedef void *(*ngx_trie_query_pt)(ngx_trie_t *trie, 26 | ngx_str_t *str, ngx_int_t *pos, ngx_uint_t mode); 27 | 28 | 29 | struct ngx_trie_node_s { 30 | void *value; 31 | ngx_trie_node_t *search_clue; 32 | ngx_trie_node_t **next; 33 | 34 | unsigned key:31; 35 | unsigned greedy:1; 36 | }; 37 | 38 | 39 | struct ngx_trie_s { 40 | ngx_trie_node_t *root; 41 | ngx_pool_t *pool; 42 | ngx_trie_insert_pt insert; 43 | ngx_trie_query_pt query; 44 | ngx_trie_build_clue_pt build_clue; 45 | }; 46 | 47 | 48 | ngx_trie_t *ngx_trie_create(ngx_pool_t *pool); 49 | ngx_trie_node_t *ngx_trie_node_create(ngx_pool_t *pool); 50 | ngx_trie_node_t *ngx_trie_insert(ngx_trie_t *trie, ngx_str_t *str, 51 | ngx_uint_t mode); 52 | void *ngx_trie_query(ngx_trie_t *trie, ngx_str_t *str, ngx_int_t *pos, 53 | ngx_uint_t mode); 54 | ngx_int_t ngx_trie_build_clue(ngx_trie_t *trie); 55 | 56 | 57 | #endif /* _NGX_TRIE_H_INCLUDE_ */ 58 | --------------------------------------------------------------------------------