/* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_headers_more_util.h" #include ngx_int_t ngx_http_headers_more_parse_header(ngx_conf_t *cf, ngx_str_t *cmd_name, ngx_str_t *raw_header, ngx_array_t *headers, ngx_http_headers_more_opcode_t opcode, ngx_http_headers_more_set_header_t *handlers) { ngx_http_headers_more_header_val_t *hv; ngx_uint_t i; ngx_str_t key = ngx_null_string; ngx_str_t value = ngx_null_string; ngx_flag_t seen_end_of_key; ngx_http_compile_complex_value_t ccv; u_char *p; hv = ngx_array_push(headers); if (hv == NULL) { return NGX_ERROR; } seen_end_of_key = 0; for (i = 0; i < raw_header->len; i++) { if (key.len == 0) { if (isspace(raw_header->data[i])) { continue; } key.data = raw_header->data; key.len = 1; continue; } if (!seen_end_of_key) { if (raw_header->data[i] == ':' || isspace(raw_header->data[i])) { seen_end_of_key = 1; continue; } key.len++; continue; } if (value.len == 0) { if (raw_header->data[i] == ':' || isspace(raw_header->data[i])) { continue; } value.data = &raw_header->data[i]; value.len = 1; continue; } value.len++; } if (key.len == 0) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: no key found in the header argument: %V", cmd_name, raw_header); return NGX_ERROR; } hv->wildcard = (key.data[key.len - 1] == '*'); if (hv->wildcard && key.len<2){ ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: wildcard key too short: %V", cmd_name, raw_header); return NGX_ERROR; } hv->hash = ngx_hash_key_lc(key.data, key.len); hv->key = key; hv->offset = 0; for (i = 0; handlers[i].name.len; i++) { if (hv->key.len != handlers[i].name.len || ngx_strncasecmp(hv->key.data, handlers[i].name.data, handlers[i].name.len) != 0) { dd("hv key comparison: %s <> %s", handlers[i].name.data, hv->key.data); continue; } hv->offset = handlers[i].offset; hv->handler = handlers[i].handler; break; } if (handlers[i].name.len == 0 && handlers[i].handler) { hv->offset = handlers[i].offset; hv->handler = handlers[i].handler; } if (opcode == ngx_http_headers_more_opcode_clear) { value.len = 0; } if (value.len == 0) { ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); return NGX_OK; } /* Nginx request header value requires to be a null-terminated * C string */ p = ngx_palloc(cf->pool, value.len + 1); if (p == NULL) { return NGX_ERROR; } ngx_memcpy(p, value.data, value.len); p[value.len] = '\0'; value.data = p; value.len++; /* we should also compile the trailing '\0' */ /* compile the header value as a complex value */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value; ccv.complex_value = &hv->value; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_headers_more_parse_statuses(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *statuses) { u_char *p, *last; ngx_uint_t *s = NULL; p = value->data; last = p + value->len; for (; p != last; p++) { if (s == NULL) { if (isspace(*p)) { continue; } s = ngx_array_push(statuses); if (s == NULL) { return NGX_ERROR; } if (*p >= '0' && *p <= '9') { *s = *p - '0'; } else { ngx_log_error(NGX_LOG_ERR, log, 0, "%V: invalid digit \"%c\" found in " "the status code list \"%V\"", cmd_name, *p, value); return NGX_ERROR; } continue; } if (isspace(*p)) { dd("Parsed status %d", (int) *s); s = NULL; continue; } if (*p >= '0' && *p <= '9') { *s *= 10; *s += *p - '0'; } else { ngx_log_error(NGX_LOG_ERR, log, 0, "%V: invalid digit \"%c\" found in " "the status code list \"%V\"", cmd_name, *p, value); return NGX_ERROR; } } if (s) { dd("Parsed status %d", (int) *s); } return NGX_OK; } ngx_int_t ngx_http_headers_more_parse_types(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *types) { u_char *p, *last; ngx_str_t *t = NULL; p = value->data; last = p + value->len; for (; p != last; p++) { if (t == NULL) { if (isspace(*p) || *p == ';') { continue; } t = ngx_array_push(types); if (t == NULL) { return NGX_ERROR; } t->len = 1; t->data = p; continue; } if (isspace(*p) || *p == ';') { t = NULL; continue; } t->len++; } return NGX_OK; } ngx_int_t ngx_http_headers_more_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, ngx_uint_t i) { ngx_table_elt_t *data; ngx_list_part_t *new, *part; dd("list rm item: part %p, i %d, nalloc %d", cur, (int) i, (int) l->nalloc); data = cur->elts; dd("cur: nelts %d, nalloc %d", (int) cur->nelts, (int) l->nalloc); if (i == 0) { cur->elts = (char *) cur->elts + l->size; cur->nelts--; if (cur == l->last) { if (cur->nelts == 0) { #if 1 part = &l->part; if (part == cur) { cur->elts = (char *) cur->elts - l->size; /* do nothing */ } else { while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } l->last = part; part->next = NULL; dd("part nelts: %d", (int) part->nelts); l->nalloc = part->nelts; } #endif } else { l->nalloc--; } return NGX_OK; } if (cur->nelts == 0) { part = &l->part; if (part == cur) { ngx_http_headers_more_assert(cur->next != NULL); dd("remove 'cur' from the list by rewriting 'cur': " "l->last: %p, cur: %p, cur->next: %p, part: %p", l->last, cur, cur->next, part); if (l->last == cur->next) { dd("last is cur->next"); l->part = *(cur->next); l->last = part; l->nalloc = part->nelts; } else { l->part = *(cur->next); } } else { dd("remove 'cur' from the list"); while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } part->next = cur->next; } return NGX_OK; } return NGX_OK; } if (i == cur->nelts - 1) { cur->nelts--; if (cur == l->last) { l->nalloc = cur->nelts; } return NGX_OK; } new = ngx_palloc(l->pool, sizeof(ngx_list_part_t)); if (new == NULL) { return NGX_ERROR; } new->elts = &data[i + 1]; new->nelts = cur->nelts - i - 1; new->next = cur->next; cur->nelts = i; cur->next = new; if (cur == l->last) { l->last = new; l->nalloc = new->nelts; } return NGX_OK; }