ngx_int_t send_header_result; } ngx_child_request_context_t; typedef struct { ngx_str_t name; off_t offset; } ngx_child_request_hide_header_t; // constants static ngx_str_t ngx_http_vod_head_method = { 4, (u_char *) "HEAD " }; static ngx_str_t range_key = ngx_string("Range"); static u_char* range_lowcase_key = (u_char*)"range"; static ngx_uint_t range_hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash('r', 'a'), 'n'), 'g'), 'e'); static ngx_child_request_hide_header_t hide_headers[] = { { ngx_string("Accept"), #if (NGX_HTTP_HEADERS) offsetof(ngx_http_headers_in_t, accept) #else -1 #endif }, { ngx_string("Accept-Charset"), -1 }, { ngx_string("Accept-Datetime"), -1 }, { ngx_string("Accept-Encoding"), #if (NGX_HTTP_GZIP) offsetof(ngx_http_headers_in_t, accept_encoding) #else
static ngx_int_t mytest_upstream_process_header(ngx_http_request_t *r) { ngx_int_t rc; 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); for (;;) { rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); //解析HTTP头部 if (rc == NGX_OK) { 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; continue; } } if (rc == NGX_HTTP_PARSE_HEADER_DONE) { 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, "Data"); ngx_str_null(&h->value); h->lowcase_key = (u_char*) "data"; } return NGX_OK; } if (rc == NGX_AGAIN) return NGX_AGAIN; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[jseanj] upstream sent invalid header"); return NGX_HTTP_UPSTREAM_INVALID_HEADER; }
static char * ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { u_char *data; size_t len; ngx_int_t rv; ngx_str_t *value, v; ngx_uint_t i, key; ngx_http_map_conf_ctx_t *ctx; ngx_http_complex_value_t cv, *cvp; ngx_http_variable_value_t *var, **vp; ngx_http_compile_complex_value_t ccv; 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; } if (cf->args->nelts == 1 && ngx_strcmp(value[0].data, "volatile") == 0) { ctx->no_cacheable = 1; return NGX_CONF_OK; } 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); } 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 (vp[i]->valid) { data = vp[i]->data; len = vp[i]->len; } else { cvp = (ngx_http_complex_value_t *) vp[i]->data; data = cvp->value.data; len = cvp->value.len; } if (value[1].len != len) { continue; } if (ngx_strncmp(value[1].data, data, 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; } v.len = value[1].len; v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); if (v.data == NULL) { return NGX_CONF_ERROR; } ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = ctx->cf; ccv.value = &v; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (cv.lengths != NULL) { cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_complex_value_t)); if (cvp == NULL) { return NGX_CONF_ERROR; } *cvp = cv; var->len = 0; var->data = (u_char *) cvp; var->valid = 0; } else { var->len = v.len; var->data = v.data; 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++; } rv = ngx_hash_add_key(&ctx->keys, &value[0], var, (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); if (rv == NGX_OK) { return NGX_CONF_OK; } if (rv == NGX_DECLINED) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid hostname or wildcard \"%V\"", &value[0]); } if (rv == NGX_BUSY) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "conflicting parameter \"%V\"", &value[0]); } return NGX_CONF_ERROR; }
ngx_int_t ngx_stream_upm_parse_header_line(ngx_stream_upm_resp_header_ctx_t *ctx, 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 = ctx->state; hash = ctx->header_hash; i = ctx->lowcase_index; for (p = b->pos; p < b->last; p++) { ch = *p; switch (state) { /* first char */ case sw_start: ctx->header_name_start = p; ctx->invalid_header = 0; 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; c = lowcase[ch]; if (c) { hash = ngx_hash(0, c); ctx->lowcase_header[0] = c; i = 1; break; } if (ch == '_') { if (allow_underscores) { hash = ngx_hash(0, ch); ctx->lowcase_header[0] = ch; i = 1; } else { ctx->invalid_header = 1; } break; } if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_HEADER; } ctx->invalid_header = 1; break; } break; /* header name */ case sw_name: c = lowcase[ch]; if (c) { hash = ngx_hash(hash, c); ctx->lowcase_header[i++] = c; i &= (NGX_HTTP_LC_HEADER_LEN - 1); break; } if (ch == '_') { if (allow_underscores) { hash = ngx_hash(hash, ch); ctx->lowcase_header[i++] = ch; i &= (NGX_HTTP_LC_HEADER_LEN - 1); } else { ctx->invalid_header = 1; } break; } if (ch == ':') { ctx->header_name_end = p; state = sw_space_before_value; 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; } /* IIS may send the duplicate "HTTP/1.1 ..." lines */ if (ch == '/' //&& ctx->upstream && p - ctx->header_name_start == 4 && ngx_strncmp(ctx->header_name_start, "HTTP", 4) == 0) { state = sw_ignore_line; break; } if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_HEADER; } ctx->invalid_header = 1; break; /* 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; case '\0': return NGX_HTTP_PARSE_INVALID_HEADER; 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; 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; ctx->state = state; ctx->header_hash = hash; ctx->lowcase_index = i; return NGX_AGAIN; done: b->pos = p + 1; ctx->state = sw_start; ctx->header_hash = hash; ctx->lowcase_index = i; return NGX_OK; header_done: b->pos = p + 1; ctx->state = sw_start; return NGX_HTTP_PARSE_HEADER_DONE; }
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; }
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; }
static ngx_int_t ngx_http_vod_extract_uri_params( ngx_http_request_t* r, ngx_hash_t* params_hash, ngx_str_t* uri, media_sequence_t* sequence, uint32_t* clip_id, media_clip_source_t* source_clip, media_clip_t** result) { ngx_http_vod_uri_param_def_t* param_def = NULL; request_context_t request_context; media_clip_t* rate_filter = NULL; ngx_uint_t cur_key_hash = 0; ngx_str_t cur_param; ngx_int_t rc; uint32_t parsed_params_mask = 0; uint32_t param_index; u_char param_name[MAX_URI_PARAM_NAME_LEN + 1]; u_char* param_name_end = param_name + sizeof(param_name); u_char* param_name_pos = param_name; u_char* copy_start = uri->data; u_char* cur_pos; u_char* end_pos = uri->data + uri->len; u_char* last_slash = NULL; u_char* p; // set the source defaults vod_memzero(source_clip, sizeof(*source_clip)); source_clip->base.type = MEDIA_CLIP_SOURCE; source_clip->base.id = (*clip_id)++; source_clip->clip_to = UINT_MAX; source_clip->tracks_mask[MEDIA_TYPE_AUDIO] = 0xffffffff; source_clip->tracks_mask[MEDIA_TYPE_VIDEO] = 0xffffffff; source_clip->uri = *uri; source_clip->sequence = sequence; *result = &source_clip->base; // allocate the stripped uri p = ngx_palloc(r->pool, uri->len + 1); if (p == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_extract_uri_params: ngx_palloc failed (1)"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } source_clip->stripped_uri.data = p; for (cur_pos = uri->data; cur_pos <= end_pos; cur_pos++) { if (cur_pos < end_pos && *cur_pos != '/') { if (param_name_pos < param_name_end) { *param_name_pos = ngx_tolower(*cur_pos); cur_key_hash = ngx_hash(cur_key_hash, *param_name_pos); param_name_pos++; } continue; } if (last_slash == NULL) { last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; continue; } if (param_def == NULL) { param_def = ngx_hash_find(params_hash, cur_key_hash, param_name, param_name_pos - param_name); if (param_def != NULL) { p = ngx_copy(p, copy_start, last_slash - copy_start); copy_start = last_slash; } } else { param_index = param_def - uri_param_defs; if ((parsed_params_mask & (1 << param_index)) == 0) // first instance of a param takes priority { parsed_params_mask |= (1 << param_index); cur_param.data = last_slash + 1; cur_param.len = cur_pos - (last_slash + 1); if (param_def->name_conf_offset == offsetof(ngx_http_vod_loc_conf_t, speed_param_name)) { request_context.pool = r->pool; request_context.log = r->connection->log; rc = rate_filter_create_from_string( &request_context, &cur_param, &source_clip->base, &rate_filter); if (rc != VOD_OK) { return ngx_http_vod_status_to_ngx_error(rc); } rate_filter->id = (*clip_id)++; *result = rate_filter; } else { rc = param_def->parser(&cur_param, source_clip, param_def->target_offset); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: %s parser failed %i", param_def->name, rc); return rc; } } } copy_start = cur_pos; param_def = NULL; } last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; } if (source_clip->clip_from >= source_clip->clip_to) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: clip from %uD is larger than clip to %uD", source_clip->clip_from, source_clip->clip_to); return NGX_HTTP_BAD_REQUEST; } p = ngx_copy(p, copy_start, end_pos - copy_start); *p = '\0'; source_clip->stripped_uri.len = p - source_clip->stripped_uri.data; source_clip->mapped_uri = source_clip->stripped_uri; return NGX_OK; }
static ngx_int_t ngx_http_srcache_set_content_length_header(ngx_http_request_t *r, off_t len) { ngx_table_elt_t *h, *header; u_char *p; ngx_list_part_t *part; ngx_http_request_t *pr; ngx_uint_t i; r->headers_in.content_length_n = len; if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } h->key = ngx_http_srcache_content_length_header_key; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); r->headers_in.content_length = h; p = ngx_palloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } h->value.data = p; h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('c', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'l'), 'e'), 'n'), 'g'), 't'), 'h'); dd("r content length: %.*s", (int)r->headers_in.content_length->value.len, r->headers_in.content_length->value.data); pr = r->parent; if (pr == NULL) { return NGX_OK; } /* forward the parent request's all other request headers */ part = &pr->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].key.len == sizeof("Content-Length") - 1 && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length", sizeof("Content-Length") - 1) == 0) { continue; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } *h = header[i]; } /* XXX maybe we should set those built-in header slot in * ngx_http_headers_in_t too? */ return NGX_OK; }
static ngx_int_t ngx_http_xrlt_init_subrequest_headers(ngx_http_request_t *sr, off_t len) { ngx_table_elt_t *h; u_char *p; memset(&sr->headers_in, 0, sizeof(ngx_http_headers_in_t)); sr->headers_in.content_length_n = len; if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } h = ngx_list_push(&sr->headers_in.headers); if (h == NULL) { return NGX_ERROR; } h->key = ngx_http_xrlt_content_length_key; h->lowcase_key = ngx_pnalloc(sr->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); sr->headers_in.content_length = h; p = ngx_palloc(sr->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } h->value.data = p; h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('c', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'l'), 'e'), 'n'), 'g'), 't'), 'h'); return NGX_OK; }
static ngx_int_t mytest_upstream_process_header(ngx_http_request_t *r) { ngx_int_t rc; ngx_table_elt_t *h; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; //这里将upstream模块配置项ngx_http_upstream_main_conf_t取了 //出来,目的只有1个,对将要转发给下游客户端的http响应头部作统一 //处理。该结构体中存储了需要做统一处理的http头部名称和回调方法 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); //循环的解析所有的http头部 for ( ;; ) { // http框架提供了基础性的ngx_http_parse_header_line //方法,它用于解析http头部 rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); //返回NGX_OK表示解析出一行http头部 if (rc == NGX_OK) { //向headers_in.headers这个ngx_list_t链表中添加http头部 h = ngx_list_push(&r->upstream->headers_in.headers); if (h == NULL) { return NGX_ERROR; } //以下开始构造刚刚添加到headers链表中的http头部 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; //必须由内存池中分配存放http头部的内存 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); } //upstream模块会对一些http头部做特殊处理 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; } continue; } //返回NGX_HTTP_PARSE_HEADER_DONE表示响应中所有的http头部都解析 //完毕,接下来再接收到的都将是http包体 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { //如果之前解析http头部时没有发现server和date头部,以下会 //根据http协议添加这两个头部 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"; } return NGX_OK; } //如果返回NGX_AGAIN则表示状态机还没有解析到完整的http头部, //要求upstream模块继续接收新的字符流再交由process_header //回调方法解析 if (rc == NGX_AGAIN) { return NGX_AGAIN; } //其他返回值都是非法的 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_parallel_init_fiber( ngx_http_request_t *r, ngx_uint_t header_in_count, ngx_flag_t proxy_as_is, ngx_http_parallel_fiber_ctx_t* fiber) { ngx_table_elt_t *h; ngx_int_t rc; // create the post-subrequest fiber->psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (fiber->psr == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_parallel_init_fiber: ngx_palloc failed"); return NGX_ERROR; } fiber->psr->handler = ngx_http_parallel_subrequest_finished_handler; fiber->psr->data = fiber; // copy the input headers fiber->headers_in = r->headers_in; if (proxy_as_is) { return NGX_OK; } // copy the headers list in order to update it rc = ngx_http_parallel_list_copy( r, &fiber->headers_in.headers, &r->headers_in.headers, header_in_count + 1, sizeof(ngx_table_elt_t)); if (rc != NGX_OK) { return rc; } ngx_http_parallel_update_headers(r, &fiber->headers_in); // add a range header if (fiber->headers_in.range == NULL) { h = ngx_list_push(&fiber->headers_in.headers); if (h == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_parallel_init_fiber: ngx_list_push failed"); return NGX_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash('r', 'a'), 'n'), 'g'), 'e'); h->key.data = (u_char*)"Range"; h->key.len = sizeof("Range") - 1; h->lowcase_key = (u_char*)"range"; fiber->headers_in.range = h; } else { h = fiber->headers_in.range; } h->value.data = ngx_pnalloc(r->pool, sizeof(RANGE_FORMAT) + 2 * NGX_OFF_T_LEN); if (h->value.data == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_parallel_init_fiber: ngx_pnalloc failed"); return NGX_ERROR; } return NGX_OK; }
&ngx_http_parallel_module_ctx, /* module context */ ngx_http_parallel_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_uint_t content_range_hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( 'c', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'r'), 'a'), 'n'), 'g'), 'e'); static ngx_str_t content_range_name = ngx_string("content-range"); // globals static ngx_http_output_body_filter_pt ngx_http_next_body_filter; // Note: the code below is a slightly modified version of ngx_http_range_parse, // it has a few limitations compared to the code found there: // 1. only a single range is supported // 2. suffix ranges (e.g -100) are not supported static ngx_int_t ngx_http_parallel_range_parse(ngx_http_request_t *r, ngx_http_range_t* result) { u_char *p; off_t start, end, cutoff, cutlim;
// like Nginx rewrite keywords // used like this: // => http code 3xx location in browser // => internal redirection in nginx static mrb_value ngx_http_mruby_redirect(mrb_state *mrb, mrb_value self) { int argc; u_char *str; ngx_int_t rc; mrb_value uri, code; ngx_str_t ns; ngx_http_request_t *r; ngx_http_mruby_ctx_t *ctx; r = ngx_http_mruby_get_request(); ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); argc = mrb_get_args(mrb, "o|oo", &uri, &code); if (argc == 2) { rc = mrb_fixnum(code); } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } uri = mrb_obj_as_string(mrb, uri); // save location uri to ns ns.data = (u_char *)RSTRING_PTR(uri); ns.len = RSTRING_LEN(uri); if (ns.len == 0) { return mrb_nil_value(); } // if uri start with scheme prefix // return 3xx for redirect // else generate a internal redirection and response to raw request // request.path is not changed if (ngx_strncmp(ns.data, "http://", sizeof("http://") - 1) == 0 || ngx_strncmp(ns.data, "https://", sizeof("https://") - 1) == 0 || ngx_strncmp(ns.data, "$scheme", sizeof("$scheme") - 1) == 0) { if ((str = ngx_pnalloc(r->pool, ns.len + 1)) == NULL) { return self; } ngx_memcpy(str, ns.data, ns.len); str[ns.len] = '\0'; // build redirect location r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return self; } r->headers_out.location->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('l', 'o'), 'c'), 'a'), 't'), 'i'), 'o'), 'n'); r->headers_out.location->value.data = ns.data; r->headers_out.location->value.len = ns.len; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.status = rc; ctx->exited = 1; ctx->exit_code = rc; } else { ctx->exited = 1; ctx->exit_code = ngx_http_internal_redirect(r, &ns, &r->args); } return self; }
static char * ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { ngx_int_t rc, index; ngx_str_t *value, file, 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) { file = value[1]; if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); return ngx_conf_parse(cf, &file); } 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 == (ngx_int_t) var[i].data) { goto found; } } var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t)); 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 *) index; vp = ngx_array_push(&ctx->var_values); if (vp == NULL) { return NGX_CONF_ERROR; } *vp = var; 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)); 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; }
static ngx_int_t ngx_lua_http_parse_header_line(ngx_lua_http_ctx_t *ctx) { 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"; ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "lua http parse header line"); state = ctx->state; hash = ctx->header_hash; i = ctx->lowcase_index; for (p = ctx->response->pos; p < ctx->response->last; p++) { ch = *p; switch (state) { /* first char */ case sw_start: ctx->header_name_start = p; ctx->invalid_header = 0; 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; c = lowcase[ch]; if (c) { hash = ngx_hash(0, c); ctx->lowcase_header[0] = c; i = 1; break; } ctx->invalid_header = 1; break; } break; /* header name */ case sw_name: c = lowcase[ch]; if (c) { hash = ngx_hash(hash, c); ctx->lowcase_header[i++] = c; i &= (NGX_HTTP_LC_HEADER_LEN - 1); break; } if (ch == '_') { hash = ngx_hash(hash, ch); ctx->lowcase_header[i++] = ch; i &= (NGX_HTTP_LC_HEADER_LEN - 1); break; } if (ch == ':') { ctx->header_name_end = p; state = sw_space_before_value; 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; } /* IIS may send the duplicate "HTTP/1.1 ..." lines */ if (ch == '/' && p - ctx->header_name_start == 4 && ngx_strncmp(ctx->header_name_start, "HTTP", 4) == 0) { state = sw_ignore_line; break; } ctx->invalid_header = 1; break; /* 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; /* 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_ERROR; } break; /* 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; ctx->header_hash = hash; ctx->lowcase_index = i; return NGX_AGAIN; done: ctx->response->pos = p + 1; ctx->state = sw_start; ctx->header_hash = hash; ctx->lowcase_index = i; return NGX_OK; header_done: ctx->response->pos = p + 1; ctx->state = sw_start; return NGX_DONE; }
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]; 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 #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; goto valid_scheme; } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) { ref += 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; } buf[i] = ngx_tolower(*p); key = ngx_hash(key, buf[i++]); if (i == 256) { goto invalid; } } uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref); if (uri) { goto uri; } #if (NGX_PCRE) if (rlcf->regex) { ngx_int_t rc; ngx_str_t referer; referer.len = len - 7; 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 ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r) { u_char ch, *key, *val, *lowcase_key; size_t len, allocated; ngx_buf_t *b; ngx_str_t *content_length; ngx_uint_t i, n, hash, header_params; 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_scgi_loc_conf_t *scf; ngx_http_script_len_code_pt lcode; static ngx_str_t zero = ngx_string("0"); content_length = r->headers_in.content_length ? &r->headers_in.content_length->value : &zero; 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 (scf->params_len) { ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); ngx_http_script_flush_no_cacheable_variables(r, scf->flushes); le.flushed = 1; le.ip = scf->params_len->elts; le.request = r; while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; len += lcode(&le); while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; len += lcode(&le) + 1; } le.ip += sizeof(uintptr_t); } } if (scf->upstream.pass_request_headers) { allocated = 0; lowcase_key = NULL; if (scf->header_params) { ignored = ngx_palloc(r->pool, scf->header_params * 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 (scf->header_params) { 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(&scf->headers_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_snprintf(b->last, NGX_SIZE_T_LEN + 1 + sizeof("CONTENT_LENGTH") + NGX_OFF_T_LEN + 1, "%ui:CONTENT_LENGTH%Z%V%Z", len, content_length); if (scf->params_len) { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); e.ip = scf->params->elts; e.pos = b->last; e.request = r; e.flushed = 1; while (*(uintptr_t *) e.ip) { #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 int ngx_http_lua_ngx_redirect(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_int_t rc; int n; u_char *p; u_char *uri; size_t len; ngx_http_request_t *r; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments"); } p = (u_char *) luaL_checklstring(L, 1, &len); if (n == 2) { rc = (ngx_int_t) luaL_checknumber(L, 2); if (rc != NGX_HTTP_MOVED_TEMPORARILY && rc != NGX_HTTP_MOVED_PERMANENTLY) { return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY and " "ngx.HTTP_MOVED_PERMANENTLY are allowed"); } } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_http_lua_check_if_abortable(L, ctx); if (ctx->headers_sent) { return luaL_error(L, "attempt to call ngx.redirect after sending out " "the headers"); } uri = ngx_palloc(r->pool, len); if (uri == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(uri, p, len); r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return luaL_error(L, "out of memory"); } r->headers_out.location->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('l', 'o'), 'c'), 'a'), 't'), 'i'), 'o'), 'n'); #if 0 dd("location hash: %lu == %lu", (unsigned long) r->headers_out.location->hash, (unsigned long) ngx_hash_key_lc((u_char *) "Location", sizeof("Location") - 1)); #endif r->headers_out.location->value.len = len; r->headers_out.location->value.data = uri; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.status = rc; ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua redirect to \"%V\" with code %i", &r->headers_out.location->value, ctx->exit_code); return lua_yield(L, 0); }
//mytest_upstream_process_header方法可以解析HTTP响应头部,而这里只是简单的 //把上游服务器发送的HTTP头部添加到了请求r->upstream->headers_in.headers链表 //中。 static ngx_int_t mytest_upstream_process_header(ngx_http_request_t *r) { ngx_int_t rc; ngx_table_elt_t *h; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; //这里将upstream模块配置项ngx_http_upstream_main_conf_t取出来,对将要转发给 //下游客户端的HTTP响应头部进行统一处理 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); //循环的解析所有的HTTP头部 for(;;){ //HTTP框架提供了基础性的ngx_http_parse_header_line方法,它用于解析HTTP头部 rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); //返回NGX_OK时,表示解析出一行HTTP头部 if(rc == NGX_OK){ //向headers_in.headers这个ngx_list_t链表中添加HTTP头部 h = ngx_list_push(&r->upstream->headers_in.headers); if(h == NULL){ return NGX_ERROR; } //下面开始构造刚刚添加到headers链表中的HTTP头部 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; //必须在内存池中非配存放HTTP头部的内存空间 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->value.data[h->value.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); } //upstream模块会对一些HTTP头部作特殊处理 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; } continue; } //返回NGX_HTTP_PARSE_HEADER_DONE时,表示响应中的所有的HTTP头部都解析完毕,接下来 //在接收到的都将是HTTP包体 if(rc == NGX_HTTP_PARSE_HEADER_DONE) { //如果之前解析HTTP头部时没有发现server和date头部,那么下面会根据HTTP协议 //规范添加这两个头部 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"; } return NGX_OK; } //如果返回NGX_AGAIN,则表示状态机还没有解析到完整的HTTP头部,此时要求upstream模块 //继续接收新的字符流,然后交由process_header回调方法解析 if(rc == NGX_AGAIN){ return NGX_AGAIN; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent invalid header"); return NGX_HTTP_UPSTREAM_INVALID_HEADER; //当mytest_upstream_process_header返回NGX_OK后 //upstream模块开始把上游的包体直接转发到下游客户端 } }
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; }
static ngx_int_t process_header(ngx_http_request_t *r) { ngx_int_t rc; ngx_uint_t i; 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); for ( ;; ) { #if NGINX_VERSION_NUM >= 7000 rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); #else rc = ngx_http_parse_header_line(r, &r->upstream->buffer); #endif 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_HTTP_INTERNAL_SERVER_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_palloc(r->pool, h->key.len + 1 + h->value.len + 1 + h->key.len); if (h->key.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_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_cpystrn(h->key.data, r->header_name_start, h->key.len + 1); ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1); if (h->key.len == r->lowcase_index) { ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { for (i = 0; i < h->key.len; i++) { h->lowcase_key[i] = ngx_tolower(h->key.data[i]); } } 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_HTTP_INTERNAL_SERVER_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"); /* * if no "Server" and "Date" in header line, * then add the default headers */ if (r->upstream->headers_in.server == NULL) { h = ngx_list_push(&r->upstream->headers_in.headers); if (h == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); h->key.len = sizeof("Server") - 1; h->key.data = (u_char *) "Server"; h->value.data = (u_char *) (NGINX_VER " + Phusion Passenger " PASSENGER_VERSION " (mod_rails/mod_rack)"); h->value.len = ngx_strlen(h->value.data); 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_HTTP_INTERNAL_SERVER_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); h->key.len = sizeof("Date") - 1; h->key.data = (u_char *) "Date"; h->value.len = 0; h->value.data = NULL; h->lowcase_key = (u_char *) "date"; } 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 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; ngx_http_core_loc_conf_t *clcf; umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_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"); /* * if no "Server" and "Date" in header line, * then add the default headers */ if (r->upstream->headers_in.server == NULL) { h = ngx_list_push(&r->upstream->headers_in.headers); if (h == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); h->key.len = sizeof("Server") - 1; h->key.data = (u_char *) "Server"; if (!passenger_main_conf.show_version_in_header) { if (clcf->server_tokens) { h->value.data = (u_char *) (NGINX_VER " + " PROGRAM_NAME); } else { h->value.data = (u_char *) ("nginx + " PROGRAM_NAME); } } else { if (clcf->server_tokens) { h->value.data = (u_char *) (NGINX_VER " + " PROGRAM_NAME " " PASSENGER_VERSION); } else { h->value.data = (u_char *) ("nginx + " PROGRAM_NAME " " PASSENGER_VERSION); } } h->value.len = ngx_strlen(h->value.data); 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_HTTP_INTERNAL_SERVER_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); h->key.len = sizeof("Date") - 1; h->key.data = (u_char *) "Date"; h->value.len = 0; h->value.data = NULL; h->lowcase_key = (u_char *) "date"; } /* Process "Status" header. */ 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: /* Supported since Nginx 1.3.15. */ #ifdef NGX_HTTP_SWITCHING_PROTOCOLS if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS && r->headers_in.upgrade) { u->upgrade = 1; } #endif 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 hexin_http_percn_set_content_length_header(ngx_http_request_t *r, off_t len) { ngx_table_elt_t *h, *header; u_char *p; ngx_list_part_t *part; ngx_http_request_t *pr; ngx_uint_t i; r->headers_in.content_length_n = len; if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } h->key = hexin_http_percn_content_length_header_key; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); r->headers_in.content_length = h; p = ngx_palloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } h->value.data = p; h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('C', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'L'), 'e'),'n'), 'g'), 't'), 'h'); pr = r->parent; if (pr == NULL) { return NGX_OK; } part = &pr->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].key.len == sizeof("Content-Length") - 1 && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length", sizeof("Content-Length") - 1) == 0) { continue; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } *h = header[i]; } return NGX_OK; }