static ngx_int_t ngx_http_addition_header_filter(ngx_http_request_t *r) { ngx_http_addition_ctx_t *ctx; ngx_http_addition_conf_t *conf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module); if (conf->before_body.len == 0 && conf->after_body.len == 0) { return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_sub_header_filter(ngx_http_request_t *r) { ngx_http_sub_ctx_t *ctx; ngx_http_sub_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); if (slcf->match.len == 0 || r->headers_out.content_length_n == 0 || ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); ctx->match = slcf->match; ctx->last_out = &ctx->out; r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); ngx_http_clear_last_modified(r); } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_set_expires_type(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { ngx_http_headers_expires_time_t *et; if (conf->enable_types) { et = ngx_http_test_content_type(r, &conf->types); if (et == NULL) { et = ngx_http_test_content_type_wildcard(r, &conf->types); } if (et != NULL) { if (et->expires == NGX_HTTP_EXPIRES_OFF) { return NGX_OK; } return ngx_http_set_expires(r, et); } } if (conf->default_expires.expires != NGX_HTTP_EXPIRES_OFF && conf->default_expires.expires != NGX_HTTP_EXPIRES_UNSET) { return ngx_http_set_expires(r, &conf->default_expires); } return NGX_OK; }
static ngx_int_t ngx_http_gunzip_header_filter(ngx_http_request_t *r) { ngx_http_gunzip_ctx_t *ctx; ngx_http_gunzip_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module); /* TODO support multiple content-codings */ /* TODO ignore content encoding? */ if (conf->enable == NGX_HTTP_GUNZIP_OFF || r->headers_out.content_encoding == NULL || r->headers_out.content_encoding->value.len != 4 || ngx_strncasecmp(r->headers_out.content_encoding->value.data, (u_char *) "gzip", 4) != 0) { return ngx_http_next_header_filter(r); } r->gzip_vary = 1; if (conf->enable == NGX_HTTP_GUNZIP_ON) { if (!r->gzip_tested) { if (ngx_http_gzip_ok(r) == NGX_OK) { return ngx_http_next_header_filter(r); } } else if (r->gzip_ok) { return ngx_http_next_header_filter(r); } } else if (conf->enable == NGX_HTTP_GUNZIP_ALWAYS && ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module); ctx->request = r; r->filter_need_in_memory = 1; r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_trim_header_filter(ngx_http_request_t *r) { ngx_int_t rc; ngx_str_t flag; ngx_http_trim_ctx_t *ctx; ngx_http_trim_loc_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_trim_filter_module); if (!conf->trim_enable || r->headers_out.status != NGX_HTTP_OK || (r->method & NGX_HTTP_HEAD) || r->headers_out.content_length_n == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } rc = ngx_http_arg(r, (u_char *) NGX_HTTP_TRIM_FLAG, sizeof(NGX_HTTP_TRIM_FLAG) - 1, &flag); if(rc == NGX_OK && flag.len == sizeof("off") - 1 && ngx_strncmp(flag.data, "off", sizeof("off") - 1) == 0) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_trim_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ctx->prev = ' '; ctx->first_line = 1; ngx_http_set_ctx(r, ctx, ngx_http_trim_filter_module); r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_subs_header_filter(ngx_http_request_t *r) { ngx_http_subs_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_subs_filter_module); if (slcf->sub_pairs->nelts == 0 || r->header_only || r->headers_out.content_type.len == 0 || r->headers_out.content_length_n == 0 || r->headers_out.status != NGX_HTTP_OK) { return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } /* Don't do substitution with the compressed content */ if (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "http subs filter header ignored, this may be a " "compressed response."); return ngx_http_next_header_filter(r); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http subs filter header \"%V\"", &r->uri); if (ngx_http_subs_init_context(r) == NGX_ERROR) { return NGX_ERROR; } r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); ngx_http_clear_last_modified(r); } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_sub_header_filter(ngx_http_request_t *r) { ngx_http_sub_ctx_t *ctx; ngx_http_sub_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); if (!slcf->wm_struct || r->headers_out.content_length_n == 0 || ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ctx->tmp.data = ngx_pnalloc(r->pool, slcf->tmp_buf_size); if (ctx->tmp.data == NULL) { return NGX_ERROR; } ctx->tmp.len = slcf->tmp_buf_size; ctx->repl = ngx_pcalloc(r->pool, slcf->values->nelts * sizeof(ngx_str_t)); ctx->wm_struct = slcf->wm_struct; ctx->values = slcf->values->elts; ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); ctx->last_out = &ctx->out; r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); ngx_http_clear_last_modified(r); ngx_http_clear_etag(r); } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_minify_header_filter(ngx_http_request_t *r) { ngx_http_minify_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_minify_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } ngx_http_clear_content_length(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_xslt_header_filter(ngx_http_request_t *r) { ngx_http_xslt_filter_ctx_t *ctx; ngx_http_xslt_filter_loc_conf_t *conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xslt filter header"); if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); if (conf->sheets.nelts == 0 || ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module); if (ctx) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module); r->main_filter_need_in_memory = 1; return NGX_OK; }
static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) { ngx_int_t charset, source_charset; ngx_str_t *mc, *from, *to, s; ngx_uint_t n; ngx_http_charset_t *charsets; ngx_http_charset_ctx_t *ctx; ngx_http_variable_value_t *vv; ngx_http_charset_loc_conf_t *lcf, *mlcf; ngx_http_charset_main_conf_t *mcf; mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); charsets = mcf->charsets.elts; n = mcf->charsets.nelts; /* destination charset */ if (r == r->main) { if (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) { return ngx_http_next_header_filter(r); } if (r->headers_out.content_type.len == 0) { return ngx_http_next_header_filter(r); } if (r->headers_out.override_charset && r->headers_out.override_charset->len) { charset = ngx_http_charset_get_charset(charsets, n, r->headers_out.override_charset); if (charset == NGX_HTTP_NO_CHARSET) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unknown charset \"%V\" to override", r->headers_out.override_charset); return ngx_http_next_header_filter(r); } } else { mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); charset = mlcf->charset; if (charset == NGX_HTTP_NO_CHARSET) { return ngx_http_next_header_filter(r); } if (r->headers_out.charset.len) { if (mlcf->override_charset == 0) { return ngx_http_next_header_filter(r); } } else { if (ngx_http_test_content_type(r, &mlcf->types) == NULL) { return ngx_http_next_header_filter(r); } } if (charset >= NGX_HTTP_CHARSET_VAR) { vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); if (vv == NULL || vv->not_found) { return NGX_ERROR; } s.len = vv->len; s.data = vv->data; charset = ngx_http_charset_get_charset(charsets, n, &s); } } } else { ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); if (ctx == NULL) { mc = &r->main->headers_out.charset; if (mc->len == 0) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); charset = ngx_http_charset_get_charset(charsets, n, mc); ctx->charset = charset; } else { charset = ctx->charset; } } /* source charset */ if (r->headers_out.charset.len == 0) { lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); source_charset = lcf->source_charset; if (source_charset >= NGX_HTTP_CHARSET_VAR) { vv = ngx_http_get_indexed_variable(r, source_charset - NGX_HTTP_CHARSET_VAR); if (vv == NULL || vv->not_found) { return NGX_ERROR; } s.len = vv->len; s.data = vv->data; source_charset = ngx_http_charset_get_charset(charsets, n, &s); } if (charset != NGX_HTTP_NO_CHARSET) { return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, source_charset); } if (source_charset == NGX_CONF_UNSET) { return ngx_http_next_header_filter(r); } from = &charsets[source_charset].name; to = &r->main->headers_out.charset; goto no_charset_map; } source_charset = ngx_http_charset_get_charset(charsets, n, &r->headers_out.charset); if (charset == NGX_HTTP_NO_CHARSET || source_charset == NGX_HTTP_NO_CHARSET) { if (charset != source_charset || ngx_strcasecmp(r->main->headers_out.charset.data, r->headers_out.charset.data) != 0) { from = &r->headers_out.charset; to = (charset == NGX_HTTP_NO_CHARSET) ? &r->main->headers_out.charset: &charsets[charset].name; goto no_charset_map; } return ngx_http_next_header_filter(r); } if (source_charset != charset && (charsets[source_charset].tables == NULL || charsets[source_charset].tables[charset] == NULL)) { from = &charsets[source_charset].name; to = &charsets[charset].name; goto no_charset_map; } r->headers_out.content_type.len = r->headers_out.content_type_len; return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, source_charset); no_charset_map: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no \"charset_map\" between the charsets \"%V\" and \"%V\"", from, to); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_brotli_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h; ngx_http_brotli_ctx_t *ctx; ngx_http_brotli_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_brotli_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } if (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) { return ngx_http_next_header_filter(r); } /* Check that brotli is supported. We do not check possible q value * if brotli is supported it takes precendence over gzip if size > * brotli_min_length */ if (accept_br(r->headers_in.accept_encoding) != NGX_OK) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_brotli_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } #if (NGX_HTTP_GZIP) r->gzip_vary = 1; /* Make sure gzip does not execute */ r->gzip_tested = 1; r->gzip_ok = 0; #endif ngx_http_set_ctx(r, ctx, ngx_http_brotli_filter_module); ctx->request = r; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); ngx_str_set(&h->value, "br"); r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name) { ngx_int_t charset; ngx_http_charset_t *charsets; ngx_http_variable_value_t *vv; ngx_http_charset_loc_conf_t *mlcf; ngx_http_charset_main_conf_t *mcf; if (r->headers_out.content_type.len == 0) { return NGX_DECLINED; } if (r->headers_out.override_charset && r->headers_out.override_charset->len) { *name = *r->headers_out.override_charset; charset = ngx_http_get_charset(r, name); if (charset != NGX_HTTP_NO_CHARSET) { return charset; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unknown charset \"%V\" to override", name); return NGX_DECLINED; } mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); charset = mlcf->charset; if (charset == NGX_HTTP_CHARSET_OFF) { return NGX_DECLINED; } if (r->headers_out.charset.len) { if (mlcf->override_charset == 0) { return NGX_DECLINED; } } else { if (ngx_http_test_content_type(r, &mlcf->types) == NULL) { return NGX_DECLINED; } } if (charset < NGX_HTTP_CHARSET_VAR) { mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); charsets = mcf->charsets.elts; *name = charsets[charset].name; return charset; } vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); if (vv == NULL || vv->not_found) { return NGX_ERROR; } name->len = vv->len; name->data = vv->data; return ngx_http_get_charset(r, name); }
static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_loc_conf_t *xlcf; ngx_str_t callback; u_char *p, *src, *dst; if (r != r->main) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped in subrequests"); return ngx_http_next_header_filter(r); } xlcf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if (!xlcf->get_enabled) { return ngx_http_next_header_filter(r); } if (r->method != NGX_HTTP_GET) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to the unmatched request method: %V", &r->method_name); return ngx_http_next_header_filter(r); } if (xlcf->check_status) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_CREATED) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched response status " "\"%ui\"", r->headers_out.status); return ngx_http_next_header_filter(r); } } if (xlcf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg " "specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &xlcf->input_types) == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched Content-Type response " "header"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, xlcf->callback_arg.data, xlcf->callback_arg.len, &callback) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped: no GET argument \"%V\" specified in " "the request", &xlcf->callback_arg); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback(callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * ctx->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = xlcf->output_type; r->headers_out.content_type_len = xlcf->output_type.len; r->headers_out.content_type_lowcase = NULL; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss output Content-Type header \"%V\"", &xlcf->output_type); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (xlcf->override_status && r->headers_out.status >= NGX_HTTP_SPECIAL_RESPONSE) { r->headers_out.status = NGX_HTTP_OK; } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_conf_t *conf; ngx_str_t callback; u_char *p, *src, *dst; if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { dd("skipped: status not 200 or in subrequest"); return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if ( ! conf->get_enabled || r->method != NGX_HTTP_GET) { dd("skipped: get_enabled disabled or the current method is not GET"); return ngx_http_next_header_filter(r); } if (conf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &conf->input_types) == NULL) { dd("skipped: content type test not passed"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, conf->callback_arg.data, conf->callback_arg.len, &callback) != NGX_OK) { dd("skipped: no callback arg found in the current request: %.*s", conf->callback_arg.len, conf->callback_arg.data); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback((char *) callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * conf->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = conf->output_type; r->headers_out.content_type_len = conf->output_type.len; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_concat_handler(ngx_http_request_t *r) { off_t length; size_t root, last_len; time_t last_modified; u_char *p, *v, *e, *last, *last_type; ngx_int_t rc; ngx_str_t *uri, *filename, path; ngx_buf_t *b; ngx_uint_t i, j, level; ngx_flag_t timestamp; ngx_array_t uris; ngx_chain_t out, **last_out, *cl; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *ccf; ngx_http_concat_loc_conf_t *clcf; if (r->uri.data[r->uri.len - 1] != '/') { return NGX_DECLINED; } if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_DECLINED; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_concat_module); if (!clcf->enable) { return NGX_DECLINED; } /* the length of args must be greater than or equal to 2 */ if (r->args.len < 2 || r->args.data[0] != '?') { return NGX_DECLINED; } rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } last = ngx_http_map_uri_to_path(r, &path, &root, 0); if (last == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } path.len = last - path.data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http concat root: \"%V\"", &path); ccf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); #if (NGX_SUPPRESS_WARN) ngx_memzero(&uris, sizeof(ngx_array_t)); #endif if (ngx_array_init(&uris, r->pool, 8, sizeof(ngx_str_t)) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } e = r->args.data + r->args.len; for (p = r->args.data + 1, v = p, timestamp = 0; p != e; p++) { if (*p == ',') { if (p == v || timestamp == 1) { v = p + 1; timestamp = 0; continue; } rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path, p, v); if (rc != NGX_OK) { return rc; } v = p + 1; } else if (*p == '?') { if (timestamp == 1) { v = p; continue; } rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path, p, v); if (rc != NGX_OK) { return rc; } v = p; timestamp = 1; } } if (p - v > 0 && timestamp == 0) { rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path, p, v); if (rc != NGX_OK) { return rc; } } last_modified = 0; last_len = 0; last_out = NULL; b = NULL; last_type = NULL; length = 0; uri = uris.elts; for (i = 0; i < uris.nelts; i++) { filename = uri + i; for (j = filename->len - 1; j > 1; j--) { if (filename->data[j] == '.' && filename->data[j - 1] != '/') { r->exten.len = filename->len - j - 1; r->exten.data = &filename->data[j + 1]; break; } else if (filename->data[j] == '/') { break; } } r->headers_out.content_type.len = 0; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.content_type_lowcase = NULL; if (ngx_http_test_content_type(r, &clcf->types) == NULL) { return NGX_HTTP_BAD_REQUEST; } if (clcf->unique) { /* test if all the content types are the same */ if ((i > 0) && (last_len != r->headers_out.content_type_len || (last_type != NULL && r->headers_out.content_type_lowcase != NULL && ngx_memcmp(last_type, r->headers_out.content_type_lowcase, last_len) != 0))) { return NGX_HTTP_BAD_REQUEST; } last_len = r->headers_out.content_type_len; last_type = r->headers_out.content_type_lowcase; } ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.read_ahead = ccf->read_ahead; of.directio = ccf->directio; of.valid = ccf->open_file_cache_valid; of.min_uses = ccf->open_file_cache_min_uses; of.errors = ccf->open_file_cache_errors; of.events = ccf->open_file_cache_events; if (ngx_open_cached_file(ccf->open_file_cache, filename, &of, r->pool) != NGX_OK) { switch (of.err) { case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR; case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG: level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break; case NGX_EACCES: level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break; default: level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; } if (rc != NGX_HTTP_NOT_FOUND || ccf->log_not_found) { ngx_log_error(level, r->connection->log, of.err, "%s \"%V\" failed", of.failed, filename); } if (clcf->ignore_file_error && (rc == NGX_HTTP_NOT_FOUND || rc == NGX_HTTP_FORBIDDEN)) { continue; } return rc; } if (!of.is_file) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, "\"%V\" is not a regular file", filename); if (clcf->ignore_file_error) { continue; } return NGX_HTTP_NOT_FOUND; } if (of.size == 0) { continue; } length += of.size; if (last_out == NULL) { last_modified = of.mtime; } else { if (of.mtime > last_modified) { last_modified = of.mtime; } } b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->file_pos = 0; b->file_last = of.size; b->in_file = b->file_last ? 1 : 0; b->file->fd = of.fd; b->file->name = *filename; b->file->log = r->connection->log; b->file->directio = of.is_directio; if (last_out == NULL) { out.buf = b; last_out = &out.next; out.next = NULL; } else { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = b; *last_out = cl; last_out = &cl->next; cl->next = NULL; } if (i + 1 == uris.nelts || clcf->delimiter.len == 0) { continue; } b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->pos = clcf->delimiter.data; b->last = b->pos + clcf->delimiter.len; b->memory = 1; length += clcf->delimiter.len; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cl->buf = b; *last_out = cl; last_out = &cl->next; cl->next = NULL; } r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = length; r->headers_out.last_modified_time = last_modified; if (ngx_http_set_etag(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_weak_etag(r); if (b == NULL) { r->header_only = 1; } rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } if (b != NULL) { b->last_in_chain = 1; b->last_buf = 1; } return ngx_http_output_filter(r, &out); }
static ngx_int_t ngx_http_replace_header_filter(ngx_http_request_t *r) { size_t size; ngx_str_t skip; ngx_pool_cleanup_t *cln; ngx_http_replace_ctx_t *ctx; ngx_http_replace_loc_conf_t *rlcf; rlcf = ngx_http_get_module_loc_conf(r, ngx_http_replace_filter_module); dd("replace header filter"); if (rlcf->regexes.nelts == 0 || r->headers_out.content_length_n == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || ngx_http_test_content_type(r, &rlcf->types) == NULL) { return ngx_http_next_header_filter(r); } dd("skip: %p", rlcf->skip); if (rlcf->skip != NULL) { if (ngx_http_complex_value(r, rlcf->skip, &skip) != NGX_OK) { return NGX_ERROR; } if (skip.len && (skip.len != 1 || skip.data[0] != '0')) { return ngx_http_next_header_filter(r); } } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_replace_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ctx->last_special = &ctx->special; ctx->last_pending = &ctx->pending; ctx->last_pending2 = &ctx->pending2; ctx->last_captured = &ctx->captured; ctx->sub = ngx_pcalloc(r->pool, rlcf->multi_replace.nelts * sizeof(ngx_str_t)); if (ctx->sub == NULL) { return NGX_ERROR; } ctx->ovector = ngx_palloc(r->pool, rlcf->ovecsize); if (ctx->ovector == NULL) { return NGX_ERROR; } size = ngx_align(rlcf->regexes.nelts, 8) / 8; ctx->disabled = ngx_pcalloc(r->pool, size); if (ctx->disabled == NULL) { return NGX_ERROR; } ctx->vm_pool = sre_create_pool(1024); if (ctx->vm_pool == NULL) { return NGX_ERROR; } dd("created vm pool %p", ctx->vm_pool); cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { sre_destroy_pool(ctx->vm_pool); return NGX_ERROR; } cln->data = ctx->vm_pool; cln->handler = ngx_http_replace_cleanup_pool; ctx->vm_ctx = sre_vm_pike_create_ctx(ctx->vm_pool, rlcf->program, ctx->ovector, rlcf->ovecsize); if (ctx->vm_ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_replace_filter_module); ctx->last_out = &ctx->out; r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); if (rlcf->last_modified == NGX_HTTP_REPLACE_CLEAR_LAST_MODIFIED) { ngx_http_clear_last_modified(r); } } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_brotli_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h, *ae; ngx_http_brotli_ctx_t *ctx; ngx_http_brotli_conf_t *conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http brotli header filter"); conf = ngx_http_get_module_loc_conf(r, ngx_http_brotli_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } /* Check that brotli is supported. We do not check possible q value * if brotli is supported it takes precendence over gzip if size > * brotli_min_length */ ae = r->headers_in.accept_encoding; if(!ae) { return ngx_http_next_header_filter(r); } /* Since there is no reason for the br string to be present * unless brotli is accepted either as "br" or "brotli" we * just check for "br" */ if (!ngx_strstrn(ae->value.data, "br", 1)) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_brotli_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } if (ngx_strstrn(ae->value.data, "brotli", 5)) { ctx->br = 0; } else { ctx->br = 1; } #if (NGX_HTTP_GZIP) r->gzip_vary = 1; /* Make sure gzip does not execute */ r->gzip_tested = 1; r->gzip_ok = 0; #endif ngx_http_set_ctx(r, ctx, ngx_http_brotli_filter_module); ctx->request = r; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); if (ctx->br) { ngx_str_set(&h->value, "br"); } else { ngx_str_set(&h->value, "brotli"); } r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h; ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } r->gzip_vary = 1; #if (NGX_HTTP_DEGRADATION) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) { return ngx_http_next_header_filter(r); } } #endif if (!r->gzip_tested) { if (ngx_http_gzip_ok(r) != NGX_OK) { return ngx_http_next_header_filter(r); } } else if (!r->gzip_ok) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module); ctx->request = r; ctx->buffering = (conf->postpone_gzipping != 0); ngx_http_gzip_filter_memory(r, ctx); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); ngx_str_set(&h->value, "gzip"); r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
// Response header filter static ngx_int_t ngx_http_jsonp_header_filter( ngx_http_request_t *r ) { ngx_http_jsonp_conf_t * cf; ngx_http_jsonp_ctx_t * ctx; ngx_http_variable_value_t * callback; // Getting the current configuration object cf = ngx_http_get_module_loc_conf(r, ngx_http_jsonp_filter_module); if (cf->enable && r->headers_out.status == NGX_HTTP_OK && !r->header_only ) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http jsonp filter"); // Do we have a content type matching the ones provided // in the configuration? if ( ngx_http_test_content_type(r, &cf->mimetypes) == NULL ) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "http jsonp filter: enabled but not configured for this mimetype"); } else { // Get the callback name from variable // and store it in the context callback = ngx_http_get_indexed_variable(r, cf->variable_index); if (callback == NULL || callback->not_found || callback->len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "http jsonp filter: the \"%V\" variable is not set", &ngx_http_jsonp_callback_variable_name); // We will not return an error on this case // return NGX_ERROR } else { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http jsonp filter: json callback is \"%v\"", callback); // Allocating a new request context for the body filter ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_jsonp_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } // Store the variable in the context ctx->callback.len = callback->len; ctx->callback.data = callback->data; ngx_http_set_ctx(r, ctx, ngx_http_jsonp_filter_module); // JSONP is has a text/javascript mimetype, let's change the Content-Type // header for the response r->headers_out.content_type = ngx_http_jsonp_mimetype; r->headers_out.content_type_len = ngx_http_jsonp_mimetype.len; // Modifying the content lenght if it is set, // adding the length of the json padding if (r->headers_out.content_length_n != -1) { r->headers_out.content_length_n += callback->len + 3; } } } } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_sub_header_filter(ngx_http_request_t *r) { ngx_str_t *m; ngx_uint_t i, j, n; ngx_http_sub_ctx_t *ctx; ngx_http_sub_pair_t *pairs; ngx_http_sub_match_t *matches; ngx_http_sub_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); if (slcf->pairs == NULL || r->headers_out.content_length_n == 0 || ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } if (slcf->dynamic == 0) { ctx->tables = slcf->tables; ctx->matches = slcf->matches; } else { pairs = slcf->pairs->elts; n = slcf->pairs->nelts; matches = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_match_t) * n); if (matches == NULL) { return NGX_ERROR; } j = 0; for (i = 0; i < n; i++) { matches[j].value = &pairs[i].value; if (pairs[i].match.lengths == NULL) { matches[j].match = pairs[i].match.value; j++; continue; } m = &matches[j].match; if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) { return NGX_ERROR; } if (m->len == 0) { continue; } ngx_strlow(m->data, m->data, m->len); j++; } if (j == 0) { return ngx_http_next_header_filter(r); } ctx->matches = ngx_palloc(r->pool, sizeof(ngx_array_t)); if (ctx->matches == NULL) { return NGX_ERROR; } ctx->matches->elts = matches; ctx->matches->nelts = j; ctx->tables = ngx_palloc(r->pool, sizeof(ngx_http_sub_tables_t)); if (ctx->tables == NULL) { return NGX_ERROR; } ngx_http_sub_init_tables(ctx->tables, ctx->matches->elts, ctx->matches->nelts); } ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); ctx->saved.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1); if (ctx->saved.data == NULL) { return NGX_ERROR; } ctx->looked.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1); if (ctx->looked.data == NULL) { return NGX_ERROR; } ctx->offset = ctx->tables->min_match_len - 1; ctx->last_out = &ctx->out; r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); if (!slcf->last_modified) { ngx_http_clear_last_modified(r); ngx_http_clear_etag(r); } else { ngx_http_weak_etag(r); } } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_minify_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_buf_t *b; ngx_int_t rc; ngx_str_t *filename; ngx_uint_t level; ngx_chain_t *cl; ngx_open_file_info_t of; ngx_http_minify_conf_t *conf; ngx_http_core_loc_conf_t *ccf; ccf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); conf = ngx_http_get_module_loc_conf(r,ngx_http_minify_filter_module); if (!conf->enable){ return ngx_http_next_body_filter(r,in); } if(ngx_http_test_content_type(r, &conf->types) == NULL){ return ngx_http_next_body_filter(r,in); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http minify filter"); for (cl = in; cl; cl = cl->next) { b = cl->buf; if (cl->buf->in_file){ ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.read_ahead = ccf->read_ahead; of.directio = ccf->directio; of.valid = ccf->open_file_cache_valid; of.min_uses = ccf->open_file_cache_min_uses; of.errors = ccf->open_file_cache_errors; of.events = ccf->open_file_cache_events; filename = &cl->buf->file->name; if (ngx_open_cached_file(ccf->open_file_cache, filename, &of, r->pool) != NGX_OK) { switch (of.err) { case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR; case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG: level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break; case NGX_EACCES: level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break; default: level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; } if (rc != NGX_HTTP_NOT_FOUND || ccf->log_not_found) { ngx_log_error(level, r->connection->log, of.err, "%s \"%V\" failed", of.failed, filename); } return rc; } if (!of.is_file) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, "\"%V\" is not a regular file", filename); return NGX_HTTP_NOT_FOUND; } if (of.size == 0) { continue; } ngx_http_minify_buf(b,r,&of); } else { if (b->pos == NULL) { continue; } ngx_http_minify_buf_in_memory(b,r); } } return ngx_http_next_body_filter(r,in); }