Initial commit
This commit is contained in:
@@ -0,0 +1,469 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
in_addr_t mask;
|
||||
in_addr_t addr;
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_http_access_rule_t;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
typedef struct {
|
||||
struct in6_addr addr;
|
||||
struct in6_addr mask;
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_http_access_rule6_t;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_http_access_rule_un_t;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *rules; /* array of ngx_http_access_rule_t */
|
||||
#if (NGX_HAVE_INET6)
|
||||
ngx_array_t *rules6; /* array of ngx_http_access_rule6_t */
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
ngx_array_t *rules_un; /* array of ngx_http_access_rule_un_t */
|
||||
#endif
|
||||
} ngx_http_access_loc_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,
|
||||
ngx_http_access_loc_conf_t *alcf, in_addr_t addr);
|
||||
#if (NGX_HAVE_INET6)
|
||||
static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,
|
||||
ngx_http_access_loc_conf_t *alcf, u_char *p);
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
static ngx_int_t ngx_http_access_unix(ngx_http_request_t *r,
|
||||
ngx_http_access_loc_conf_t *alcf);
|
||||
#endif
|
||||
static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);
|
||||
static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_access_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_access_commands[] = {
|
||||
|
||||
{ ngx_string("allow"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_http_access_rule,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("deny"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_http_access_rule,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_access_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_access_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_access_create_loc_conf, /* create location configuration */
|
||||
ngx_http_access_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_access_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_access_module_ctx, /* module context */
|
||||
ngx_http_access_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_int_t
|
||||
ngx_http_access_handler(ngx_http_request_t *r)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
ngx_http_access_loc_conf_t *alcf;
|
||||
#if (NGX_HAVE_INET6)
|
||||
u_char *p;
|
||||
in_addr_t addr;
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
|
||||
alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
|
||||
|
||||
switch (r->connection->sockaddr->sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
if (alcf->rules) {
|
||||
sin = (struct sockaddr_in *) r->connection->sockaddr;
|
||||
return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
|
||||
}
|
||||
break;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
|
||||
p = sin6->sin6_addr.s6_addr;
|
||||
|
||||
if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
|
||||
addr = p[12] << 24;
|
||||
addr += p[13] << 16;
|
||||
addr += p[14] << 8;
|
||||
addr += p[15];
|
||||
return ngx_http_access_inet(r, alcf, htonl(addr));
|
||||
}
|
||||
|
||||
if (alcf->rules6) {
|
||||
return ngx_http_access_inet6(r, alcf, p);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
case AF_UNIX:
|
||||
if (alcf->rules_un) {
|
||||
return ngx_http_access_unix(r, alcf);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
|
||||
in_addr_t addr)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_http_access_rule_t *rule;
|
||||
|
||||
rule = alcf->rules->elts;
|
||||
for (i = 0; i < alcf->rules->nelts; i++) {
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"access: %08XD %08XD %08XD",
|
||||
addr, rule[i].mask, rule[i].addr);
|
||||
|
||||
if ((addr & rule[i].mask) == rule[i].addr) {
|
||||
return ngx_http_access_found(r, rule[i].deny);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_access_inet6(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
|
||||
u_char *p)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
ngx_uint_t i;
|
||||
ngx_http_access_rule6_t *rule6;
|
||||
|
||||
rule6 = alcf->rules6->elts;
|
||||
for (i = 0; i < alcf->rules6->nelts; i++) {
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
{
|
||||
size_t cl, ml, al;
|
||||
u_char ct[NGX_INET6_ADDRSTRLEN];
|
||||
u_char mt[NGX_INET6_ADDRSTRLEN];
|
||||
u_char at[NGX_INET6_ADDRSTRLEN];
|
||||
|
||||
cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);
|
||||
ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);
|
||||
al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);
|
||||
|
||||
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"access: %*s %*s %*s", cl, ct, ml, mt, al, at);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (n = 0; n < 16; n++) {
|
||||
if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_http_access_found(r, rule6[i].deny);
|
||||
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_access_unix(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_http_access_rule_un_t *rule_un;
|
||||
|
||||
rule_un = alcf->rules_un->elts;
|
||||
for (i = 0; i < alcf->rules_un->nelts; i++) {
|
||||
|
||||
/* TODO: check path */
|
||||
if (1) {
|
||||
return ngx_http_access_found(r, rule_un[i].deny);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (deny) {
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"access forbidden by rule");
|
||||
}
|
||||
|
||||
return NGX_HTTP_FORBIDDEN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_access_loc_conf_t *alcf = conf;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t all;
|
||||
ngx_str_t *value;
|
||||
ngx_cidr_t cidr;
|
||||
ngx_http_access_rule_t *rule;
|
||||
#if (NGX_HAVE_INET6)
|
||||
ngx_http_access_rule6_t *rule6;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
ngx_http_access_rule_un_t *rule_un;
|
||||
#endif
|
||||
|
||||
ngx_memzero(&cidr, sizeof(ngx_cidr_t));
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);
|
||||
|
||||
if (!all) {
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) {
|
||||
cidr.family = AF_UNIX;
|
||||
rc = NGX_OK;
|
||||
|
||||
} else {
|
||||
rc = ngx_ptocidr(&value[1], &cidr);
|
||||
}
|
||||
|
||||
#else
|
||||
rc = ngx_ptocidr(&value[1], &cidr);
|
||||
#endif
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"low address bits of %V are meaningless", &value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (cidr.family == AF_INET || all) {
|
||||
|
||||
if (alcf->rules == NULL) {
|
||||
alcf->rules = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_http_access_rule_t));
|
||||
if (alcf->rules == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule = ngx_array_push(alcf->rules);
|
||||
if (rule == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule->mask = cidr.u.in.mask;
|
||||
rule->addr = cidr.u.in.addr;
|
||||
rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
if (cidr.family == AF_INET6 || all) {
|
||||
|
||||
if (alcf->rules6 == NULL) {
|
||||
alcf->rules6 = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_http_access_rule6_t));
|
||||
if (alcf->rules6 == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule6 = ngx_array_push(alcf->rules6);
|
||||
if (rule6 == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule6->mask = cidr.u.in6.mask;
|
||||
rule6->addr = cidr.u.in6.addr;
|
||||
rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
if (cidr.family == AF_UNIX || all) {
|
||||
|
||||
if (alcf->rules_un == NULL) {
|
||||
alcf->rules_un = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_http_access_rule_un_t));
|
||||
if (alcf->rules_un == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule_un = ngx_array_push(alcf->rules_un);
|
||||
if (rule_un == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_access_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_access_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_access_loc_conf_t *prev = parent;
|
||||
ngx_http_access_loc_conf_t *conf = child;
|
||||
|
||||
if (conf->rules == NULL
|
||||
#if (NGX_HAVE_INET6)
|
||||
&& conf->rules6 == NULL
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
&& conf->rules_un == NULL
|
||||
#endif
|
||||
) {
|
||||
conf->rules = prev->rules;
|
||||
#if (NGX_HAVE_INET6)
|
||||
conf->rules6 = prev->rules6;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
conf->rules_un = prev->rules_un;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_access_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_access_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t before_body;
|
||||
ngx_str_t after_body;
|
||||
|
||||
ngx_hash_t types;
|
||||
ngx_array_t *types_keys;
|
||||
} ngx_http_addition_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t before_body_sent;
|
||||
} ngx_http_addition_ctx_t;
|
||||
|
||||
|
||||
static void *ngx_http_addition_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_addition_commands[] = {
|
||||
|
||||
{ ngx_string("add_before_body"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_addition_conf_t, before_body),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("add_after_body"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_addition_conf_t, after_body),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("addition_types"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_types_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_addition_conf_t, types_keys),
|
||||
&ngx_http_html_default_types[0] },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_addition_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_addition_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_addition_create_conf, /* create location configuration */
|
||||
ngx_http_addition_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_addition_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_addition_filter_module_ctx, /* module context */
|
||||
ngx_http_addition_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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
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);
|
||||
ngx_http_weak_etag(r);
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t last;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *sr;
|
||||
ngx_http_addition_ctx_t *ctx;
|
||||
ngx_http_addition_conf_t *conf;
|
||||
|
||||
if (in == NULL || r->header_only) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
|
||||
|
||||
if (!ctx->before_body_sent) {
|
||||
ctx->before_body_sent = 1;
|
||||
|
||||
if (conf->before_body.len) {
|
||||
if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->after_body.len == 0) {
|
||||
ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
last = 0;
|
||||
|
||||
for (cl = in; cl; cl = cl->next) {
|
||||
if (cl->buf->last_buf) {
|
||||
cl->buf->last_buf = 0;
|
||||
cl->buf->sync = 1;
|
||||
last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ngx_http_next_body_filter(r, in);
|
||||
|
||||
if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
|
||||
|
||||
return ngx_http_send_special(r, NGX_HTTP_LAST);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_addition_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_addition_header_filter;
|
||||
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_addition_body_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_addition_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_addition_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->before_body = { 0, NULL };
|
||||
* conf->after_body = { 0, NULL };
|
||||
* conf->types = { NULL };
|
||||
* conf->types_keys = NULL;
|
||||
*/
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_addition_conf_t *prev = parent;
|
||||
ngx_http_addition_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");
|
||||
ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");
|
||||
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_crypt.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_AUTH_BUF_SIZE 2048
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t passwd;
|
||||
} ngx_http_auth_basic_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_complex_value_t *realm;
|
||||
ngx_http_complex_value_t user_file;
|
||||
} ngx_http_auth_basic_loc_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
|
||||
ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm);
|
||||
static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
|
||||
ngx_str_t *realm);
|
||||
static void ngx_http_auth_basic_close(ngx_file_t *file);
|
||||
static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
|
||||
static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_auth_basic_commands[] = {
|
||||
|
||||
{ ngx_string("auth_basic"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_http_set_complex_value_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_auth_basic_loc_conf_t, realm),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("auth_basic_user_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_http_auth_basic_user_file,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_auth_basic_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_auth_basic_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_auth_basic_create_loc_conf, /* create location configuration */
|
||||
ngx_http_auth_basic_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_auth_basic_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_auth_basic_module_ctx, /* module context */
|
||||
ngx_http_auth_basic_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_int_t
|
||||
ngx_http_auth_basic_handler(ngx_http_request_t *r)
|
||||
{
|
||||
off_t offset;
|
||||
ssize_t n;
|
||||
ngx_fd_t fd;
|
||||
ngx_int_t rc;
|
||||
ngx_err_t err;
|
||||
ngx_str_t pwd, realm, user_file;
|
||||
ngx_uint_t i, level, login, left, passwd;
|
||||
ngx_file_t file;
|
||||
ngx_http_auth_basic_ctx_t *ctx;
|
||||
ngx_http_auth_basic_loc_conf_t *alcf;
|
||||
u_char buf[NGX_HTTP_AUTH_BUF_SIZE];
|
||||
enum {
|
||||
sw_login,
|
||||
sw_passwd,
|
||||
sw_skip
|
||||
} state;
|
||||
|
||||
alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);
|
||||
|
||||
if (alcf->realm == NULL || alcf->user_file.value.data == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);
|
||||
|
||||
if (ctx) {
|
||||
return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd,
|
||||
&realm);
|
||||
}
|
||||
|
||||
rc = ngx_http_auth_basic_user(r);
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"no user/password was provided for basic authentication");
|
||||
|
||||
return ngx_http_auth_basic_set_realm(r, &realm);
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
|
||||
|
||||
if (fd == NGX_INVALID_FILE) {
|
||||
err = ngx_errno;
|
||||
|
||||
if (err == NGX_ENOENT) {
|
||||
level = NGX_LOG_ERR;
|
||||
rc = NGX_HTTP_FORBIDDEN;
|
||||
|
||||
} else {
|
||||
level = NGX_LOG_CRIT;
|
||||
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_error(level, r->connection->log, err,
|
||||
ngx_open_file_n " \"%s\" failed", user_file.data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_memzero(&file, sizeof(ngx_file_t));
|
||||
|
||||
file.fd = fd;
|
||||
file.name = user_file;
|
||||
file.log = r->connection->log;
|
||||
|
||||
state = sw_login;
|
||||
passwd = 0;
|
||||
login = 0;
|
||||
left = 0;
|
||||
offset = 0;
|
||||
|
||||
for ( ;; ) {
|
||||
i = left;
|
||||
|
||||
n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
|
||||
offset);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
ngx_http_auth_basic_close(&file);
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = left; i < left + n; i++) {
|
||||
switch (state) {
|
||||
|
||||
case sw_login:
|
||||
if (login == 0) {
|
||||
|
||||
if (buf[i] == '#' || buf[i] == CR) {
|
||||
state = sw_skip;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf[i] == LF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf[i] != r->headers_in.user.data[login]) {
|
||||
state = sw_skip;
|
||||
break;
|
||||
}
|
||||
|
||||
if (login == r->headers_in.user.len) {
|
||||
state = sw_passwd;
|
||||
passwd = i + 1;
|
||||
}
|
||||
|
||||
login++;
|
||||
|
||||
break;
|
||||
|
||||
case sw_passwd:
|
||||
if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
|
||||
buf[i] = '\0';
|
||||
|
||||
ngx_http_auth_basic_close(&file);
|
||||
|
||||
pwd.len = i - passwd;
|
||||
pwd.data = &buf[passwd];
|
||||
|
||||
return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,
|
||||
&realm);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case sw_skip:
|
||||
if (buf[i] == LF) {
|
||||
state = sw_login;
|
||||
login = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == sw_passwd) {
|
||||
left = left + n - passwd;
|
||||
ngx_memmove(buf, &buf[passwd], left);
|
||||
passwd = 0;
|
||||
|
||||
} else {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
offset += n;
|
||||
}
|
||||
|
||||
ngx_http_auth_basic_close(&file);
|
||||
|
||||
if (state == sw_passwd) {
|
||||
pwd.len = i - passwd;
|
||||
pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
|
||||
if (pwd.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
|
||||
|
||||
return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm);
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"user \"%V\" was not found in \"%V\"",
|
||||
&r->headers_in.user, &user_file);
|
||||
|
||||
return ngx_http_auth_basic_set_realm(r, &realm);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
|
||||
ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
u_char *encrypted;
|
||||
|
||||
rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
|
||||
&encrypted);
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"rc: %d user: \"%V\" salt: \"%s\"",
|
||||
rc, &r->headers_in.user, passwd->data);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
if (ngx_strcmp(encrypted, passwd->data) == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"encrypted: \"%s\"", encrypted);
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"user \"%V\": password mismatch",
|
||||
&r->headers_in.user);
|
||||
|
||||
return ngx_http_auth_basic_set_realm(r, realm);
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/* rc == NGX_AGAIN */
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);
|
||||
|
||||
ctx->passwd.len = passwd->len;
|
||||
passwd->len++;
|
||||
|
||||
ctx->passwd.data = ngx_pstrdup(r->pool, passwd);
|
||||
if (ctx->passwd.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* TODO: add mutex event */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
|
||||
{
|
||||
size_t len;
|
||||
u_char *basic, *p;
|
||||
|
||||
r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
|
||||
if (r->headers_out.www_authenticate == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
len = sizeof("Basic realm=\"\"") - 1 + realm->len;
|
||||
|
||||
basic = ngx_pnalloc(r->pool, len);
|
||||
if (basic == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
|
||||
p = ngx_cpymem(p, realm->data, realm->len);
|
||||
*p = '"';
|
||||
|
||||
r->headers_out.www_authenticate->hash = 1;
|
||||
ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");
|
||||
r->headers_out.www_authenticate->value.data = basic;
|
||||
r->headers_out.www_authenticate->value.len = len;
|
||||
|
||||
return NGX_HTTP_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_http_auth_basic_close(ngx_file_t *file)
|
||||
{
|
||||
if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
|
||||
ngx_close_file_n " \"%s\" failed", file->name.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_auth_basic_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_auth_basic_loc_conf_t *prev = parent;
|
||||
ngx_http_auth_basic_loc_conf_t *conf = child;
|
||||
|
||||
if (conf->realm == NULL) {
|
||||
conf->realm = prev->realm;
|
||||
}
|
||||
|
||||
if (conf->user_file.value.data == NULL) {
|
||||
conf->user_file = prev->user_file;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_basic_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_auth_basic_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_auth_basic_loc_conf_t *alcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
if (alcf->user_file.value.data) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &alcf->user_file;
|
||||
ccv.zero = 1;
|
||||
ccv.conf_prefix = 1;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t uri;
|
||||
ngx_array_t *vars;
|
||||
} ngx_http_auth_request_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t done;
|
||||
ngx_uint_t status;
|
||||
ngx_http_request_t *subrequest;
|
||||
} ngx_http_auth_request_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t index;
|
||||
ngx_http_complex_value_t value;
|
||||
ngx_http_set_variable_pt set_handler;
|
||||
} ngx_http_auth_request_variable_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,
|
||||
void *data, ngx_int_t rc);
|
||||
static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,
|
||||
ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);
|
||||
static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_auth_request_commands[] = {
|
||||
|
||||
{ ngx_string("auth_request"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_auth_request,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("auth_request_set"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
|
||||
ngx_http_auth_request_set,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_auth_request_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_auth_request_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_auth_request_create_conf, /* create location configuration */
|
||||
ngx_http_auth_request_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_auth_request_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_auth_request_module_ctx, /* module context */
|
||||
ngx_http_auth_request_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_int_t
|
||||
ngx_http_auth_request_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_table_elt_t *h, *ho;
|
||||
ngx_http_request_t *sr;
|
||||
ngx_http_post_subrequest_t *ps;
|
||||
ngx_http_auth_request_ctx_t *ctx;
|
||||
ngx_http_auth_request_conf_t *arcf;
|
||||
|
||||
arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
|
||||
|
||||
if (arcf->uri.len == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"auth request handler");
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (!ctx->done) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* as soon as we are done - explicitly set variables to make
|
||||
* sure they will be available after internal redirects
|
||||
*/
|
||||
|
||||
if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* return appropriate status */
|
||||
|
||||
if (ctx->status == NGX_HTTP_FORBIDDEN) {
|
||||
return ctx->status;
|
||||
}
|
||||
|
||||
if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
|
||||
sr = ctx->subrequest;
|
||||
|
||||
h = sr->headers_out.www_authenticate;
|
||||
|
||||
if (!h && sr->upstream) {
|
||||
h = sr->upstream->headers_in.www_authenticate;
|
||||
}
|
||||
|
||||
if (h) {
|
||||
ho = ngx_list_push(&r->headers_out.headers);
|
||||
if (ho == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*ho = *h;
|
||||
|
||||
r->headers_out.www_authenticate = ho;
|
||||
}
|
||||
|
||||
return ctx->status;
|
||||
}
|
||||
|
||||
if (ctx->status >= NGX_HTTP_OK
|
||||
&& ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"auth request unexpected status: %d", ctx->status);
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
|
||||
if (ps == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ps->handler = ngx_http_auth_request_done;
|
||||
ps->data = ctx;
|
||||
|
||||
if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
|
||||
NGX_HTTP_SUBREQUEST_WAITED)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate fake request body to avoid attempts to read it and to make
|
||||
* sure real body file (if already read) won't be closed by upstream
|
||||
*/
|
||||
|
||||
sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
|
||||
if (sr->request_body == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
sr->header_only = 1;
|
||||
|
||||
ctx->subrequest = sr;
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
|
||||
{
|
||||
ngx_http_auth_request_ctx_t *ctx = data;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"auth request done s:%d", r->headers_out.status);
|
||||
|
||||
ctx->done = 1;
|
||||
ctx->status = r->headers_out.status;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_request_set_variables(ngx_http_request_t *r,
|
||||
ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
|
||||
{
|
||||
ngx_str_t val;
|
||||
ngx_http_variable_t *v;
|
||||
ngx_http_variable_value_t *vv;
|
||||
ngx_http_auth_request_variable_t *av, *last;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"auth request set variables");
|
||||
|
||||
if (arcf->vars == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
|
||||
v = cmcf->variables.elts;
|
||||
|
||||
av = arcf->vars->elts;
|
||||
last = av + arcf->vars->nelts;
|
||||
|
||||
while (av < last) {
|
||||
/*
|
||||
* explicitly set new value to make sure it will be available after
|
||||
* internal redirects
|
||||
*/
|
||||
|
||||
vv = &r->variables[av->index];
|
||||
|
||||
if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
vv->valid = 1;
|
||||
vv->not_found = 0;
|
||||
vv->data = val.data;
|
||||
vv->len = val.len;
|
||||
|
||||
if (av->set_handler) {
|
||||
/*
|
||||
* set_handler only available in cmcf->variables_keys, so we store
|
||||
* it explicitly
|
||||
*/
|
||||
|
||||
av->set_handler(r, vv, v[av->index].data);
|
||||
}
|
||||
|
||||
av++;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_request_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"auth request variable");
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_auth_request_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_auth_request_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->uri = { 0, NULL };
|
||||
*/
|
||||
|
||||
conf->vars = NGX_CONF_UNSET_PTR;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_auth_request_conf_t *prev = parent;
|
||||
ngx_http_auth_request_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
|
||||
ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_request_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_auth_request_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_auth_request_conf_t *arcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (arcf->uri.data != NULL) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "off") == 0) {
|
||||
arcf->uri.len = 0;
|
||||
arcf->uri.data = (u_char *) "";
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
arcf->uri = value[1];
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_auth_request_conf_t *arcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_http_variable_t *v;
|
||||
ngx_http_auth_request_variable_t *av;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (value[1].data[0] != '$') {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid variable name \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value[1].len--;
|
||||
value[1].data++;
|
||||
|
||||
if (arcf->vars == NGX_CONF_UNSET_PTR) {
|
||||
arcf->vars = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_http_auth_request_variable_t));
|
||||
if (arcf->vars == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
av = ngx_array_push(arcf->vars);
|
||||
if (av == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (v == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
av->index = ngx_http_get_variable_index(cf, &value[1]);
|
||||
if (av->index == NGX_ERROR) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (v->get_handler == NULL) {
|
||||
v->get_handler = ngx_http_auth_request_variable;
|
||||
v->data = (uintptr_t) av;
|
||||
}
|
||||
|
||||
av->set_handler = v->set_handler;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[2];
|
||||
ccv.complex_value = &av->value;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,715 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
/*
|
||||
* The module can check browser versions conforming to the following formats:
|
||||
* X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be
|
||||
* 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
|
||||
*/
|
||||
|
||||
|
||||
#define NGX_HTTP_MODERN_BROWSER 0
|
||||
#define NGX_HTTP_ANCIENT_BROWSER 1
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char browser[12];
|
||||
size_t skip;
|
||||
size_t add;
|
||||
u_char name[12];
|
||||
} ngx_http_modern_browser_mask_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t version;
|
||||
size_t skip;
|
||||
size_t add;
|
||||
u_char name[12];
|
||||
} ngx_http_modern_browser_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_http_get_variable_pt handler;
|
||||
uintptr_t data;
|
||||
} ngx_http_browser_variable_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *modern_browsers;
|
||||
ngx_array_t *ancient_browsers;
|
||||
ngx_http_variable_value_t *modern_browser_value;
|
||||
ngx_http_variable_value_t *ancient_browser_value;
|
||||
|
||||
unsigned modern_unlisted_browsers:1;
|
||||
unsigned netscape4:1;
|
||||
} ngx_http_browser_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
|
||||
static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
|
||||
ngx_http_browser_conf_t *cf);
|
||||
|
||||
static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf);
|
||||
static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
|
||||
const void *two);
|
||||
static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_browser_commands[] = {
|
||||
|
||||
{ ngx_string("modern_browser"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_modern_browser,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ancient_browser"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_ancient_browser,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("modern_browser_value"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_modern_browser_value,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ancient_browser_value"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_ancient_browser_value,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_browser_module_ctx = {
|
||||
ngx_http_browser_add_variable, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_browser_create_conf, /* create location configuration */
|
||||
ngx_http_browser_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_browser_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_browser_module_ctx, /* module context */
|
||||
ngx_http_browser_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_http_modern_browser_mask_t ngx_http_modern_browser_masks[] = {
|
||||
|
||||
/* Opera must be the first browser to check */
|
||||
|
||||
/*
|
||||
* "Opera/7.50 (X11; FreeBSD i386; U) [en]"
|
||||
* "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50 [en]"
|
||||
* "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50 [en]"
|
||||
* "Opera/8.0 (Windows NT 5.1; U; ru)"
|
||||
* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
|
||||
* "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
|
||||
*/
|
||||
|
||||
{ "opera",
|
||||
0,
|
||||
sizeof("Opera ") - 1,
|
||||
"Opera"},
|
||||
|
||||
/* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */
|
||||
|
||||
{ "msie",
|
||||
sizeof("Mozilla/4.0 (compatible; ") - 1,
|
||||
sizeof("MSIE ") - 1,
|
||||
"MSIE "},
|
||||
|
||||
/*
|
||||
* "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"
|
||||
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"
|
||||
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206
|
||||
* Firefox/0.8"
|
||||
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)
|
||||
* Gecko/20050511 Firefox/1.0.4"
|
||||
* "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729
|
||||
* Firefox/1.5.0.5"
|
||||
*/
|
||||
|
||||
{ "gecko",
|
||||
sizeof("Mozilla/5.0 (") - 1,
|
||||
sizeof("rv:") - 1,
|
||||
"rv:"},
|
||||
|
||||
/*
|
||||
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
|
||||
* (KHTML, like Gecko) Safari/125.7"
|
||||
* "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
|
||||
* (KHTML, like Gecko) Safari/413"
|
||||
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
|
||||
* (KHTML, like Gecko) Safari/417.9.3"
|
||||
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
|
||||
* (KHTML, like Gecko) Safari/419.3"
|
||||
*/
|
||||
|
||||
{ "safari",
|
||||
sizeof("Mozilla/5.0 (") - 1,
|
||||
sizeof("Safari/") - 1,
|
||||
"Safari/"},
|
||||
|
||||
/*
|
||||
* "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
|
||||
* "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
|
||||
* "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
|
||||
* (like Gecko)"
|
||||
*/
|
||||
|
||||
{ "konqueror",
|
||||
sizeof("Mozilla/5.0 (compatible; ") - 1,
|
||||
sizeof("Konqueror/") - 1,
|
||||
"Konqueror/"},
|
||||
|
||||
{ "", 0, 0, "" }
|
||||
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_browser_variable_t ngx_http_browsers[] = {
|
||||
{ ngx_string("msie"), ngx_http_msie_variable, 0 },
|
||||
{ ngx_string("modern_browser"), ngx_http_browser_variable,
|
||||
NGX_HTTP_MODERN_BROWSER },
|
||||
{ ngx_string("ancient_browser"), ngx_http_browser_variable,
|
||||
NGX_HTTP_ANCIENT_BROWSER },
|
||||
{ ngx_null_string, NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
ngx_uint_t rc;
|
||||
ngx_http_browser_conf_t *cf;
|
||||
|
||||
cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
|
||||
|
||||
rc = ngx_http_browser(r, cf);
|
||||
|
||||
if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
|
||||
*v = *cf->modern_browser_value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
|
||||
*v = *cf->ancient_browser_value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
*v = ngx_http_variable_null_value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
|
||||
{
|
||||
size_t len;
|
||||
u_char *name, *ua, *last, c;
|
||||
ngx_str_t *ancient;
|
||||
ngx_uint_t i, version, ver, scale;
|
||||
ngx_http_modern_browser_t *modern;
|
||||
|
||||
if (r->headers_in.user_agent == NULL) {
|
||||
if (cf->modern_unlisted_browsers) {
|
||||
return NGX_HTTP_MODERN_BROWSER;
|
||||
}
|
||||
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
|
||||
ua = r->headers_in.user_agent->value.data;
|
||||
len = r->headers_in.user_agent->value.len;
|
||||
last = ua + len;
|
||||
|
||||
if (cf->modern_browsers) {
|
||||
modern = cf->modern_browsers->elts;
|
||||
|
||||
for (i = 0; i < cf->modern_browsers->nelts; i++) {
|
||||
name = ua + modern[i].skip;
|
||||
|
||||
if (name >= last) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name = (u_char *) ngx_strstr(name, modern[i].name);
|
||||
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"browser: \"%s\"", name);
|
||||
|
||||
name += modern[i].add;
|
||||
|
||||
if (name >= last) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"version: \"%ui\" \"%s\"", modern[i].version, name);
|
||||
|
||||
version = 0;
|
||||
ver = 0;
|
||||
scale = 1000000;
|
||||
|
||||
while (name < last) {
|
||||
|
||||
c = *name++;
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
ver = ver * 10 + (c - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '.') {
|
||||
version += ver * scale;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"version: \"%ui\" \"%ui\"",
|
||||
modern[i].version, version);
|
||||
|
||||
if (version > modern[i].version) {
|
||||
return NGX_HTTP_MODERN_BROWSER;
|
||||
}
|
||||
|
||||
ver = 0;
|
||||
scale /= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
version += ver * scale;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"version: \"%ui\" \"%ui\"",
|
||||
modern[i].version, version);
|
||||
|
||||
if (version >= modern[i].version) {
|
||||
return NGX_HTTP_MODERN_BROWSER;
|
||||
}
|
||||
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
|
||||
if (!cf->modern_unlisted_browsers) {
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
}
|
||||
|
||||
if (cf->netscape4) {
|
||||
if (len > sizeof("Mozilla/4.72 ") - 1
|
||||
&& ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
|
||||
&& ua[8] > '0' && ua[8] < '5')
|
||||
{
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
}
|
||||
|
||||
if (cf->ancient_browsers) {
|
||||
ancient = cf->ancient_browsers->elts;
|
||||
|
||||
for (i = 0; i < cf->ancient_browsers->nelts; i++) {
|
||||
if (len >= ancient[i].len
|
||||
&& ngx_strstr(ua, ancient[i].data) != NULL)
|
||||
{
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cf->modern_unlisted_browsers) {
|
||||
return NGX_HTTP_MODERN_BROWSER;
|
||||
}
|
||||
|
||||
return NGX_HTTP_ANCIENT_BROWSER;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
if (r->headers_in.msie) {
|
||||
*v = ngx_http_variable_true_value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
*v = ngx_http_variable_null_value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_browser_add_variable(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_browser_variable_t *var;
|
||||
ngx_http_variable_t *v;
|
||||
|
||||
for (var = ngx_http_browsers; var->name.len; var++) {
|
||||
|
||||
v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (v == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->get_handler = var->handler;
|
||||
v->data = var->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_browser_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_browser_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->modern_browsers = NULL;
|
||||
* conf->ancient_browsers = NULL;
|
||||
* conf->modern_browser_value = NULL;
|
||||
* conf->ancient_browser_value = NULL;
|
||||
*
|
||||
* conf->modern_unlisted_browsers = 0;
|
||||
* conf->netscape4 = 0;
|
||||
*/
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_browser_conf_t *prev = parent;
|
||||
ngx_http_browser_conf_t *conf = child;
|
||||
|
||||
ngx_uint_t i, n;
|
||||
ngx_http_modern_browser_t *browsers, *opera;
|
||||
|
||||
/*
|
||||
* At the merge the skip field is used to store the browser slot,
|
||||
* it will be used in sorting and then will overwritten
|
||||
* with a real skip value. The zero value means Opera.
|
||||
*/
|
||||
|
||||
if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
|
||||
conf->modern_browsers = prev->modern_browsers;
|
||||
conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;
|
||||
|
||||
} else if (conf->modern_browsers != NULL) {
|
||||
browsers = conf->modern_browsers->elts;
|
||||
|
||||
for (i = 0; i < conf->modern_browsers->nelts; i++) {
|
||||
if (browsers[i].skip == 0) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Opera may contain MSIE string, so if Opera was not enumerated
|
||||
* as modern browsers, then add it and set a unreachable version
|
||||
*/
|
||||
|
||||
opera = ngx_array_push(conf->modern_browsers);
|
||||
if (opera == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
opera->skip = 0;
|
||||
opera->version = 4001000000U;
|
||||
|
||||
browsers = conf->modern_browsers->elts;
|
||||
|
||||
found:
|
||||
|
||||
ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
|
||||
sizeof(ngx_http_modern_browser_t),
|
||||
ngx_http_modern_browser_sort);
|
||||
|
||||
for (i = 0; i < conf->modern_browsers->nelts; i++) {
|
||||
n = browsers[i].skip;
|
||||
|
||||
browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
|
||||
browsers[i].add = ngx_http_modern_browser_masks[n].add;
|
||||
(void) ngx_cpystrn(browsers[i].name,
|
||||
ngx_http_modern_browser_masks[n].name, 12);
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
|
||||
conf->ancient_browsers = prev->ancient_browsers;
|
||||
conf->netscape4 = prev->netscape4;
|
||||
}
|
||||
|
||||
if (conf->modern_browser_value == NULL) {
|
||||
conf->modern_browser_value = prev->modern_browser_value;
|
||||
}
|
||||
|
||||
if (conf->modern_browser_value == NULL) {
|
||||
conf->modern_browser_value = &ngx_http_variable_true_value;
|
||||
}
|
||||
|
||||
if (conf->ancient_browser_value == NULL) {
|
||||
conf->ancient_browser_value = prev->ancient_browser_value;
|
||||
}
|
||||
|
||||
if (conf->ancient_browser_value == NULL) {
|
||||
conf->ancient_browser_value = &ngx_http_variable_true_value;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_modern_browser_sort(const void *one, const void *two)
|
||||
{
|
||||
ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
|
||||
ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;
|
||||
|
||||
return (first->skip - second->skip);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_browser_conf_t *bcf = conf;
|
||||
|
||||
u_char c;
|
||||
ngx_str_t *value;
|
||||
ngx_uint_t i, n, version, ver, scale;
|
||||
ngx_http_modern_browser_t *browser;
|
||||
ngx_http_modern_browser_mask_t *mask;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (cf->args->nelts == 2) {
|
||||
if (ngx_strcmp(value[1].data, "unlisted") == 0) {
|
||||
bcf->modern_unlisted_browsers = 1;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (bcf->modern_browsers == NULL) {
|
||||
bcf->modern_browsers = ngx_array_create(cf->pool, 5,
|
||||
sizeof(ngx_http_modern_browser_t));
|
||||
if (bcf->modern_browsers == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
browser = ngx_array_push(bcf->modern_browsers);
|
||||
if (browser == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
mask = ngx_http_modern_browser_masks;
|
||||
|
||||
for (n = 0; mask[n].browser[0] != '\0'; n++) {
|
||||
if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"unknown browser name \"%V\"", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
|
||||
found:
|
||||
|
||||
/*
|
||||
* at this stage the skip field is used to store the browser slot,
|
||||
* it will be used in sorting in merge stage and then will overwritten
|
||||
* with a real value
|
||||
*/
|
||||
|
||||
browser->skip = n;
|
||||
|
||||
version = 0;
|
||||
ver = 0;
|
||||
scale = 1000000;
|
||||
|
||||
for (i = 0; i < value[2].len; i++) {
|
||||
|
||||
c = value[2].data[i];
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
ver = ver * 10 + (c - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '.') {
|
||||
version += ver * scale;
|
||||
ver = 0;
|
||||
scale /= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid browser version \"%V\"", &value[2]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
version += ver * scale;
|
||||
|
||||
browser->version = version;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_browser_conf_t *bcf = conf;
|
||||
|
||||
ngx_str_t *value, *browser;
|
||||
ngx_uint_t i;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
if (ngx_strcmp(value[i].data, "netscape4") == 0) {
|
||||
bcf->netscape4 = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bcf->ancient_browsers == NULL) {
|
||||
bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_str_t));
|
||||
if (bcf->ancient_browsers == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
browser = ngx_array_push(bcf->ancient_browsers);
|
||||
if (browser == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*browser = value[i];
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_browser_conf_t *bcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
bcf->modern_browser_value = ngx_palloc(cf->pool,
|
||||
sizeof(ngx_http_variable_value_t));
|
||||
if (bcf->modern_browser_value == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
bcf->modern_browser_value->len = value[1].len;
|
||||
bcf->modern_browser_value->valid = 1;
|
||||
bcf->modern_browser_value->no_cacheable = 0;
|
||||
bcf->modern_browser_value->not_found = 0;
|
||||
bcf->modern_browser_value->data = value[1].data;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_browser_conf_t *bcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
bcf->ancient_browser_value = ngx_palloc(cf->pool,
|
||||
sizeof(ngx_http_variable_value_t));
|
||||
if (bcf->ancient_browser_value == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
bcf->ancient_browser_value->len = value[1].len;
|
||||
bcf->ancient_browser_value->valid = 1;
|
||||
bcf->ancient_browser_value->no_cacheable = 0;
|
||||
bcf->ancient_browser_value->not_found = 0;
|
||||
bcf->ancient_browser_value->data = value[1].data;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
} ngx_http_chunked_filter_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_chunked_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_chunked_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_chunked_filter_module_ctx, /* module context */
|
||||
NULL, /* 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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_chunked_header_filter(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_chunked_filter_ctx_t *ctx;
|
||||
|
||||
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
|
||||
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
|
||||
|| r->headers_out.status < NGX_HTTP_OK
|
||||
|| r != r->main
|
||||
|| (r->method & NGX_HTTP_HEAD))
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
if (r->headers_out.content_length_n == -1) {
|
||||
if (r->http_version < NGX_HTTP_VERSION_11) {
|
||||
r->keepalive = 0;
|
||||
|
||||
} else {
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->chunked_transfer_encoding) {
|
||||
r->chunked = 1;
|
||||
|
||||
ctx = ngx_pcalloc(r->pool,
|
||||
sizeof(ngx_http_chunked_filter_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
|
||||
|
||||
} else {
|
||||
r->keepalive = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
u_char *chunk;
|
||||
off_t size;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *out, *cl, *tl, **ll;
|
||||
ngx_http_chunked_filter_ctx_t *ctx;
|
||||
|
||||
if (in == NULL || !r->chunked || r->header_only) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);
|
||||
|
||||
out = NULL;
|
||||
ll = &out;
|
||||
|
||||
size = 0;
|
||||
cl = in;
|
||||
|
||||
for ( ;; ) {
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http chunk: %d", ngx_buf_size(cl->buf));
|
||||
|
||||
size += ngx_buf_size(cl->buf);
|
||||
|
||||
if (cl->buf->flush
|
||||
|| cl->buf->sync
|
||||
|| ngx_buf_in_memory(cl->buf)
|
||||
|| cl->buf->in_file)
|
||||
{
|
||||
tl = ngx_alloc_chain_link(r->pool);
|
||||
if (tl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
tl->buf = cl->buf;
|
||||
*ll = tl;
|
||||
ll = &tl->next;
|
||||
}
|
||||
|
||||
if (cl->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
cl = cl->next;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = tl->buf;
|
||||
chunk = b->start;
|
||||
|
||||
if (chunk == NULL) {
|
||||
/* the "0000000000000000" is 64-bit hexadecimal string */
|
||||
|
||||
chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
|
||||
if (chunk == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->start = chunk;
|
||||
b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
|
||||
}
|
||||
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
|
||||
b->memory = 0;
|
||||
b->temporary = 1;
|
||||
b->pos = chunk;
|
||||
b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
|
||||
|
||||
tl->next = out;
|
||||
out = tl;
|
||||
}
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = tl->buf;
|
||||
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
|
||||
b->temporary = 0;
|
||||
b->memory = 1;
|
||||
b->last_buf = 1;
|
||||
b->pos = (u_char *) CRLF "0" CRLF CRLF;
|
||||
b->last = b->pos + 7;
|
||||
|
||||
cl->buf->last_buf = 0;
|
||||
|
||||
*ll = tl;
|
||||
|
||||
if (size == 0) {
|
||||
b->pos += 2;
|
||||
}
|
||||
|
||||
} else if (size > 0) {
|
||||
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (tl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = tl->buf;
|
||||
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
|
||||
b->temporary = 0;
|
||||
b->memory = 1;
|
||||
b->pos = (u_char *) CRLF;
|
||||
b->last = b->pos + 2;
|
||||
|
||||
*ll = tl;
|
||||
|
||||
} else {
|
||||
*ll = NULL;
|
||||
}
|
||||
|
||||
rc = ngx_http_next_body_filter(r, out);
|
||||
|
||||
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
|
||||
(ngx_buf_tag_t) &ngx_http_chunked_filter_module);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_chunked_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_chunked_header_filter;
|
||||
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_chunked_body_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t sbrk_size;
|
||||
} ngx_http_degradation_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t degrade;
|
||||
} ngx_http_degradation_loc_conf_t;
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_degrade[] = {
|
||||
{ ngx_string("204"), 204 },
|
||||
{ ngx_string("444"), 444 },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);
|
||||
static void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_degradation_commands[] = {
|
||||
|
||||
{ ngx_string("degradation"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_degradation,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("degrade"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_degradation_loc_conf_t, degrade),
|
||||
&ngx_http_degrade },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_degradation_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_degradation_init, /* postconfiguration */
|
||||
|
||||
ngx_http_degradation_create_main_conf, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_degradation_create_loc_conf, /* create location configuration */
|
||||
ngx_http_degradation_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_degradation_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_degradation_module_ctx, /* module context */
|
||||
ngx_http_degradation_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_int_t
|
||||
ngx_http_degradation_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_degradation_loc_conf_t *dlcf;
|
||||
|
||||
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);
|
||||
|
||||
if (dlcf->degrade && ngx_http_degraded(r)) {
|
||||
return dlcf->degrade;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
ngx_uint_t
|
||||
ngx_http_degraded(ngx_http_request_t *r)
|
||||
{
|
||||
time_t now;
|
||||
ngx_uint_t log;
|
||||
static size_t sbrk_size;
|
||||
static time_t sbrk_time;
|
||||
ngx_http_degradation_main_conf_t *dmcf;
|
||||
|
||||
dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);
|
||||
|
||||
if (dmcf->sbrk_size) {
|
||||
|
||||
log = 0;
|
||||
now = ngx_time();
|
||||
|
||||
/* lock mutex */
|
||||
|
||||
if (now != sbrk_time) {
|
||||
|
||||
/*
|
||||
* ELF/i386 is loaded at 0x08000000, 128M
|
||||
* ELF/amd64 is loaded at 0x00400000, 4M
|
||||
*
|
||||
* use a function address to subtract the loading address
|
||||
*/
|
||||
|
||||
sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);
|
||||
sbrk_time = now;
|
||||
log = 1;
|
||||
}
|
||||
|
||||
/* unlock mutex */
|
||||
|
||||
if (sbrk_size >= dmcf->sbrk_size) {
|
||||
if (log) {
|
||||
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
||||
"degradation sbrk:%uzM",
|
||||
sbrk_size / (1024 * 1024));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_degradation_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_degradation_main_conf_t *dmcf;
|
||||
|
||||
dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));
|
||||
if (dmcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dmcf;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_degradation_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_degradation_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->degrade = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_degradation_loc_conf_t *prev = parent;
|
||||
ngx_http_degradation_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_degradation_main_conf_t *dmcf = conf;
|
||||
|
||||
ngx_str_t *value, s;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) {
|
||||
|
||||
s.len = value[1].len - 5;
|
||||
s.data = value[1].data + 5;
|
||||
|
||||
dmcf->sbrk_size = ngx_parse_size(&s);
|
||||
if (dmcf->sbrk_size == (size_t) NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid sbrk size \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_degradation_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_degradation_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static char *ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
static ngx_command_t ngx_http_empty_gif_commands[] = {
|
||||
|
||||
{ ngx_string("empty_gif"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
|
||||
ngx_http_empty_gif,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
/* the minimal single pixel transparent GIF, 43 bytes */
|
||||
|
||||
static u_char ngx_empty_gif[] = {
|
||||
|
||||
'G', 'I', 'F', '8', '9', 'a', /* header */
|
||||
|
||||
/* logical screen descriptor */
|
||||
0x01, 0x00, /* logical screen width */
|
||||
0x01, 0x00, /* logical screen height */
|
||||
0x80, /* global 1-bit color table */
|
||||
0x01, /* background color #1 */
|
||||
0x00, /* no aspect ratio */
|
||||
|
||||
/* global color table */
|
||||
0x00, 0x00, 0x00, /* #0: black */
|
||||
0xff, 0xff, 0xff, /* #1: white */
|
||||
|
||||
/* graphic control extension */
|
||||
0x21, /* extension introducer */
|
||||
0xf9, /* graphic control label */
|
||||
0x04, /* block size */
|
||||
0x01, /* transparent color is given, */
|
||||
/* no disposal specified, */
|
||||
/* user input is not expected */
|
||||
0x00, 0x00, /* delay time */
|
||||
0x01, /* transparent color #1 */
|
||||
0x00, /* block terminator */
|
||||
|
||||
/* image descriptor */
|
||||
0x2c, /* image separator */
|
||||
0x00, 0x00, /* image left position */
|
||||
0x00, 0x00, /* image top position */
|
||||
0x01, 0x00, /* image width */
|
||||
0x01, 0x00, /* image height */
|
||||
0x00, /* no local color table, no interlaced */
|
||||
|
||||
/* table based image data */
|
||||
0x02, /* LZW minimum code size, */
|
||||
/* must be at least 2-bit */
|
||||
0x02, /* block size */
|
||||
0x4c, 0x01, /* compressed bytes 01_001_100, 0000000_1 */
|
||||
/* 100: clear code */
|
||||
/* 001: 1 */
|
||||
/* 101: end of information code */
|
||||
0x00, /* block terminator */
|
||||
|
||||
0x3B /* trailer */
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_empty_gif_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_empty_gif_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_empty_gif_module_ctx, /* module context */
|
||||
ngx_http_empty_gif_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_str_t ngx_http_gif_type = ngx_string("image/gif");
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_empty_gif_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_complex_value_t cv;
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
ngx_memzero(&cv, sizeof(ngx_http_complex_value_t));
|
||||
|
||||
cv.value.len = sizeof(ngx_empty_gif);
|
||||
cv.value.data = ngx_empty_gif;
|
||||
r->headers_out.last_modified_time = 23349600;
|
||||
|
||||
return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_http_empty_gif_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
static ngx_command_t ngx_http_flv_commands[] = {
|
||||
|
||||
{ ngx_string("flv"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
|
||||
ngx_http_flv,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static u_char ngx_flv_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_flv_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_flv_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_flv_module_ctx, /* module context */
|
||||
ngx_http_flv_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_int_t
|
||||
ngx_http_flv_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *last;
|
||||
off_t start, len;
|
||||
size_t root;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t level, i;
|
||||
ngx_str_t path, value;
|
||||
ngx_log_t *log;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t out[2];
|
||||
ngx_open_file_info_t of;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
if (r->uri.data[r->uri.len - 1] == '/') {
|
||||
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;
|
||||
}
|
||||
|
||||
log = r->connection->log;
|
||||
|
||||
path.len = last - path.data;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
|
||||
"http flv filename: \"%V\"", &path);
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
||||
|
||||
of.read_ahead = clcf->read_ahead;
|
||||
of.directio = clcf->directio;
|
||||
of.valid = clcf->open_file_cache_valid;
|
||||
of.min_uses = clcf->open_file_cache_min_uses;
|
||||
of.errors = clcf->open_file_cache_errors;
|
||||
of.events = clcf->open_file_cache_events;
|
||||
|
||||
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_open_cached_file(clcf->open_file_cache, &path, &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:
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
case NGX_EMLINK:
|
||||
case NGX_ELOOP:
|
||||
#endif
|
||||
|
||||
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 || clcf->log_not_found) {
|
||||
ngx_log_error(level, log, of.err,
|
||||
"%s \"%s\" failed", of.failed, path.data);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!of.is_file) {
|
||||
|
||||
if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
|
||||
ngx_close_file_n " \"%s\" failed", path.data);
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
r->root_tested = !r->error_page;
|
||||
|
||||
start = 0;
|
||||
len = of.size;
|
||||
i = 1;
|
||||
|
||||
if (r->args.len) {
|
||||
|
||||
if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
|
||||
|
||||
start = ngx_atoof(value.data, value.len);
|
||||
|
||||
if (start == NGX_ERROR || start >= len) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (start) {
|
||||
len = sizeof(ngx_flv_header) - 1 + len - start;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log->action = "sending flv to client";
|
||||
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
r->headers_out.content_length_n = len;
|
||||
r->headers_out.last_modified_time = of.mtime;
|
||||
|
||||
if (ngx_http_set_etag(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_set_content_type(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
|
||||
if (b == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
b->pos = ngx_flv_header;
|
||||
b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;
|
||||
b->memory = 1;
|
||||
|
||||
out[0].buf = b;
|
||||
out[0].next = &out[1];
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
r->allow_ranges = 1;
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
b->file_pos = start;
|
||||
b->file_last = of.size;
|
||||
|
||||
b->in_file = b->file_last ? 1: 0;
|
||||
b->last_buf = (r == r->main) ? 1 : 0;
|
||||
b->last_in_chain = 1;
|
||||
|
||||
b->file->fd = of.fd;
|
||||
b->file->name = path;
|
||||
b->file->log = log;
|
||||
b->file->directio = of.is_directio;
|
||||
|
||||
out[1].buf = b;
|
||||
out[1].next = NULL;
|
||||
|
||||
return ngx_http_output_filter(r, &out[i]);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_http_flv_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,925 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <GeoIP.h>
|
||||
#include <GeoIPCity.h>
|
||||
|
||||
|
||||
#define NGX_GEOIP_COUNTRY_CODE 0
|
||||
#define NGX_GEOIP_COUNTRY_CODE3 1
|
||||
#define NGX_GEOIP_COUNTRY_NAME 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
GeoIP *country;
|
||||
GeoIP *org;
|
||||
GeoIP *city;
|
||||
ngx_array_t *proxies; /* array of ngx_cidr_t */
|
||||
ngx_flag_t proxy_recursive;
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
unsigned country_v6:1;
|
||||
unsigned org_v6:1;
|
||||
unsigned city_v6:1;
|
||||
#endif
|
||||
} ngx_http_geoip_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t *name;
|
||||
uintptr_t data;
|
||||
} ngx_http_geoip_var_t;
|
||||
|
||||
|
||||
typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,
|
||||
u_long addr);
|
||||
|
||||
|
||||
ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {
|
||||
GeoIP_country_code_by_ipnum,
|
||||
GeoIP_country_code3_by_ipnum,
|
||||
GeoIP_country_name_by_ipnum,
|
||||
};
|
||||
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
|
||||
typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,
|
||||
geoipv6_t addr);
|
||||
|
||||
|
||||
ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {
|
||||
GeoIP_country_code_by_ipnum_v6,
|
||||
GeoIP_country_code3_by_ipnum_v6,
|
||||
GeoIP_country_name_by_ipnum_v6,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);
|
||||
|
||||
static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);
|
||||
static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);
|
||||
static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
|
||||
ngx_cidr_t *cidr);
|
||||
static void ngx_http_geoip_cleanup(void *data);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_geoip_commands[] = {
|
||||
|
||||
{ ngx_string("geoip_country"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_geoip_country,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_org"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_geoip_org,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_city"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_geoip_city,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_proxy"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_geoip_proxy,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_proxy_recursive"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_geoip_conf_t, proxy_recursive),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_geoip_module_ctx = {
|
||||
ngx_http_geoip_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_http_geoip_create_conf, /* create main configuration */
|
||||
ngx_http_geoip_init_conf, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_geoip_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_geoip_module_ctx, /* module context */
|
||||
ngx_http_geoip_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_http_variable_t ngx_http_geoip_vars[] = {
|
||||
|
||||
{ ngx_string("geoip_country_code"), NULL,
|
||||
ngx_http_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_CODE, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_country_code3"), NULL,
|
||||
ngx_http_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_country_name"), NULL,
|
||||
ngx_http_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_NAME, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_org"), NULL,
|
||||
ngx_http_geoip_org_variable,
|
||||
0, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_continent_code"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, continent_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_code"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_code3"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_code3), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_name"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_name), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_region"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, region), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_region_name"), NULL,
|
||||
ngx_http_geoip_region_name_variable,
|
||||
0, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, city), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_postal_code"), NULL,
|
||||
ngx_http_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, postal_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_latitude"), NULL,
|
||||
ngx_http_geoip_city_float_variable,
|
||||
offsetof(GeoIPRecord, latitude), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_longitude"), NULL,
|
||||
ngx_http_geoip_city_float_variable,
|
||||
offsetof(GeoIPRecord, longitude), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_dma_code"), NULL,
|
||||
ngx_http_geoip_city_int_variable,
|
||||
offsetof(GeoIPRecord, dma_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_area_code"), NULL,
|
||||
ngx_http_geoip_city_int_variable,
|
||||
offsetof(GeoIPRecord, area_code), 0, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static u_long
|
||||
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
|
||||
{
|
||||
ngx_addr_t addr;
|
||||
ngx_array_t *xfwd;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
addr.sockaddr = r->connection->sockaddr;
|
||||
addr.socklen = r->connection->socklen;
|
||||
/* addr.name = r->connection->addr_text; */
|
||||
|
||||
xfwd = &r->headers_in.x_forwarded_for;
|
||||
|
||||
if (xfwd->nelts > 0 && gcf->proxies != NULL) {
|
||||
(void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
|
||||
gcf->proxies, gcf->proxy_recursive);
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
if (addr.sockaddr->sa_family == AF_INET6) {
|
||||
u_char *p;
|
||||
in_addr_t inaddr;
|
||||
struct in6_addr *inaddr6;
|
||||
|
||||
inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
|
||||
|
||||
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
|
||||
p = inaddr6->s6_addr;
|
||||
|
||||
inaddr = p[12] << 24;
|
||||
inaddr += p[13] << 16;
|
||||
inaddr += p[14] << 8;
|
||||
inaddr += p[15];
|
||||
|
||||
return inaddr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (addr.sockaddr->sa_family != AF_INET) {
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
sin = (struct sockaddr_in *) addr.sockaddr;
|
||||
return ntohl(sin->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
|
||||
static geoipv6_t
|
||||
ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
|
||||
{
|
||||
ngx_addr_t addr;
|
||||
ngx_array_t *xfwd;
|
||||
in_addr_t addr4;
|
||||
struct in6_addr addr6;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
addr.sockaddr = r->connection->sockaddr;
|
||||
addr.socklen = r->connection->socklen;
|
||||
/* addr.name = r->connection->addr_text; */
|
||||
|
||||
xfwd = &r->headers_in.x_forwarded_for;
|
||||
|
||||
if (xfwd->nelts > 0 && gcf->proxies != NULL) {
|
||||
(void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
|
||||
gcf->proxies, gcf->proxy_recursive);
|
||||
}
|
||||
|
||||
switch (addr.sockaddr->sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
/* Produce IPv4-mapped IPv6 address. */
|
||||
sin = (struct sockaddr_in *) addr.sockaddr;
|
||||
addr4 = ntohl(sin->sin_addr.s_addr);
|
||||
|
||||
ngx_memzero(&addr6, sizeof(struct in6_addr));
|
||||
addr6.s6_addr[10] = 0xff;
|
||||
addr6.s6_addr[11] = 0xff;
|
||||
addr6.s6_addr[12] = addr4 >> 24;
|
||||
addr6.s6_addr[13] = addr4 >> 16;
|
||||
addr6.s6_addr[14] = addr4 >> 8;
|
||||
addr6.s6_addr[15] = addr4;
|
||||
return addr6;
|
||||
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) addr.sockaddr;
|
||||
return sin6->sin6_addr;
|
||||
|
||||
default:
|
||||
return in6addr_any;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_country_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_geoip_variable_handler_pt handler =
|
||||
ngx_http_geoip_country_functions[data];
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
ngx_http_geoip_variable_handler_v6_pt handler_v6 =
|
||||
ngx_http_geoip_country_v6_functions[data];
|
||||
#endif
|
||||
|
||||
const char *val;
|
||||
ngx_http_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
|
||||
|
||||
if (gcf->country == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
val = gcf->country_v6
|
||||
? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))
|
||||
: handler(gcf->country, ngx_http_geoip_addr(r, gcf));
|
||||
#else
|
||||
val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));
|
||||
#endif
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
v->len = ngx_strlen(val);
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = (u_char *) val;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_org_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
size_t len;
|
||||
char *val;
|
||||
ngx_http_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
|
||||
|
||||
if (gcf->org == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
val = gcf->org_v6
|
||||
? GeoIP_name_by_ipnum_v6(gcf->org,
|
||||
ngx_http_geoip_addr_v6(r, gcf))
|
||||
: GeoIP_name_by_ipnum(gcf->org,
|
||||
ngx_http_geoip_addr(r, gcf));
|
||||
#else
|
||||
val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));
|
||||
#endif
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(r->pool, len);
|
||||
if (v->data == NULL) {
|
||||
ngx_free(val);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
ngx_free(val);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_city_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
char *val;
|
||||
size_t len;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_http_geoip_get_city_record(r);
|
||||
if (gr == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
val = *(char **) ((char *) gr + data);
|
||||
if (val == NULL) {
|
||||
goto no_value;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(r->pool, len);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
no_value:
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
size_t len;
|
||||
const char *val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_http_geoip_get_city_record(r);
|
||||
if (gr == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
val = GeoIP_region_name_by_code(gr->country_code, gr->region);
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(r->pool, len);
|
||||
if (v->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
float val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_http_geoip_get_city_record(r);
|
||||
if (gr == NULL) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
val = *(float *) ((char *) gr + data);
|
||||
|
||||
v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
int val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_http_geoip_get_city_record(r);
|
||||
if (gr == NULL) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
val = *(int *) ((char *) gr + data);
|
||||
|
||||
v->len = ngx_sprintf(v->data, "%d", val) - v->data;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static GeoIPRecord *
|
||||
ngx_http_geoip_get_city_record(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
|
||||
|
||||
if (gcf->city) {
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
return gcf->city_v6
|
||||
? GeoIP_record_by_ipnum_v6(gcf->city,
|
||||
ngx_http_geoip_addr_v6(r, gcf))
|
||||
: GeoIP_record_by_ipnum(gcf->city,
|
||||
ngx_http_geoip_addr(r, gcf));
|
||||
#else
|
||||
return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_http_geoip_vars; v->name.len; v++) {
|
||||
var = ngx_http_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_geoip_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_pool_cleanup_t *cln;
|
||||
ngx_http_geoip_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->proxy_recursive = NGX_CONF_UNSET;
|
||||
|
||||
cln = ngx_pool_cleanup_add(cf->pool, 0);
|
||||
if (cln == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cln->handler = ngx_http_geoip_cleanup;
|
||||
cln->data = conf;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_conf_init_value(gcf->proxy_recursive, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->country) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->country == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->country->databaseType) {
|
||||
|
||||
case GEOIP_COUNTRY_EDITION:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_COUNTRY_EDITION_V6:
|
||||
|
||||
gcf->country_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP database \"%V\" type:%d",
|
||||
&value[1], gcf->country->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->org) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->org == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->org->databaseType) {
|
||||
|
||||
case GEOIP_ISP_EDITION:
|
||||
case GEOIP_ORG_EDITION:
|
||||
case GEOIP_DOMAIN_EDITION:
|
||||
case GEOIP_ASNUM_EDITION:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_ISP_EDITION_V6:
|
||||
case GEOIP_ORG_EDITION_V6:
|
||||
case GEOIP_DOMAIN_EDITION_V6:
|
||||
case GEOIP_ASNUM_EDITION_V6:
|
||||
|
||||
gcf->org_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP database \"%V\" type:%d",
|
||||
&value[1], gcf->org->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->city) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->city == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->city->databaseType) {
|
||||
|
||||
case GEOIP_CITY_EDITION_REV0:
|
||||
case GEOIP_CITY_EDITION_REV1:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_CITY_EDITION_REV0_V6:
|
||||
case GEOIP_CITY_EDITION_REV1_V6:
|
||||
|
||||
gcf->city_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP City database \"%V\" type:%d",
|
||||
&value[1], gcf->city->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_cidr_t cidr, *c;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (gcf->proxies == NULL) {
|
||||
gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
|
||||
if (gcf->proxies == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
c = ngx_array_push(gcf->proxies);
|
||||
if (c == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*c = cidr;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
|
||||
if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
|
||||
cidr->family = AF_INET;
|
||||
cidr->u.in.addr = 0xffffffff;
|
||||
cidr->u.in.mask = 0xffffffff;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
rc = ngx_ptocidr(net, cidr);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"low address bits of %V are meaningless", net);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_geoip_cleanup(void *data)
|
||||
{
|
||||
ngx_http_geoip_conf_t *gcf = data;
|
||||
|
||||
if (gcf->country) {
|
||||
GeoIP_delete(gcf->country);
|
||||
}
|
||||
|
||||
if (gcf->org) {
|
||||
GeoIP_delete(gcf->org);
|
||||
}
|
||||
|
||||
if (gcf->city) {
|
||||
GeoIP_delete(gcf->city);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,687 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_flag_t enable;
|
||||
ngx_bufs_t bufs;
|
||||
} ngx_http_gunzip_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_chain_t *in;
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
ngx_chain_t *out;
|
||||
ngx_chain_t **last_out;
|
||||
|
||||
ngx_buf_t *in_buf;
|
||||
ngx_buf_t *out_buf;
|
||||
ngx_int_t bufs;
|
||||
|
||||
unsigned started:1;
|
||||
unsigned flush:4;
|
||||
unsigned redo:1;
|
||||
unsigned done:1;
|
||||
unsigned nomem:1;
|
||||
|
||||
z_stream zstream;
|
||||
ngx_http_request_t *request;
|
||||
} ngx_http_gunzip_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx);
|
||||
|
||||
static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,
|
||||
u_int size);
|
||||
static void ngx_http_gunzip_filter_free(void *opaque, void *address);
|
||||
|
||||
static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);
|
||||
static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_gunzip_filter_commands[] = {
|
||||
|
||||
{ ngx_string("gunzip"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_gunzip_conf_t, enable),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("gunzip_buffers"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
|
||||
ngx_conf_set_bufs_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_gunzip_conf_t, bufs),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_gunzip_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_gunzip_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_gunzip_create_conf, /* create location configuration */
|
||||
ngx_http_gunzip_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_gunzip_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_gunzip_filter_module_ctx, /* module context */
|
||||
ngx_http_gunzip_filter_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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
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 always gunzip - due to configuration or module request */
|
||||
/* TODO ignore content encoding? */
|
||||
|
||||
if (!conf->enable
|
||||
|| 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 (!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_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_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
int rc;
|
||||
ngx_uint_t flush;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_gunzip_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);
|
||||
|
||||
if (ctx == NULL || ctx->done) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http gunzip filter");
|
||||
|
||||
if (!ctx->started) {
|
||||
if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (in) {
|
||||
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->nomem) {
|
||||
|
||||
/* flush busy buffers */
|
||||
|
||||
if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
cl = NULL;
|
||||
|
||||
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
|
||||
(ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
|
||||
ctx->nomem = 0;
|
||||
flush = 0;
|
||||
|
||||
} else {
|
||||
flush = ctx->busy ? 1 : 0;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
/* cycle while we can write to a client */
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
/* cycle while there is data to feed zlib and ... */
|
||||
|
||||
rc = ngx_http_gunzip_filter_add_data(r, ctx);
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* ... there are buffers to write zlib output */
|
||||
|
||||
rc = ngx_http_gunzip_filter_get_buf(r, ctx);
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ngx_http_gunzip_filter_inflate(r, ctx);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* rc == NGX_AGAIN */
|
||||
}
|
||||
|
||||
if (ctx->out == NULL && !flush) {
|
||||
return ctx->busy ? NGX_AGAIN : NGX_OK;
|
||||
}
|
||||
|
||||
rc = ngx_http_next_body_filter(r, ctx->out);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
|
||||
(ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
|
||||
ctx->last_out = &ctx->out;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"gunzip out: %p", ctx->out);
|
||||
|
||||
ctx->nomem = 0;
|
||||
flush = 0;
|
||||
|
||||
if (ctx->done) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
|
||||
failed:
|
||||
|
||||
ctx->done = 1;
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ctx->zstream.next_in = Z_NULL;
|
||||
ctx->zstream.avail_in = 0;
|
||||
|
||||
ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
|
||||
ctx->zstream.zfree = ngx_http_gunzip_filter_free;
|
||||
ctx->zstream.opaque = ctx;
|
||||
|
||||
/* windowBits +16 to decode gzip, zlib 1.2.0.4+ */
|
||||
rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);
|
||||
|
||||
if (rc != Z_OK) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"inflateInit2() failed: %d", rc);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->started = 1;
|
||||
|
||||
ctx->last_out = &ctx->out;
|
||||
ctx->flush = Z_NO_FLUSH;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"gunzip in: %p", ctx->in);
|
||||
|
||||
if (ctx->in == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ctx->in_buf = ctx->in->buf;
|
||||
ctx->in = ctx->in->next;
|
||||
|
||||
ctx->zstream.next_in = ctx->in_buf->pos;
|
||||
ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"gunzip in_buf:%p ni:%p ai:%ud",
|
||||
ctx->in_buf,
|
||||
ctx->zstream.next_in, ctx->zstream.avail_in);
|
||||
|
||||
if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {
|
||||
ctx->flush = Z_FINISH;
|
||||
|
||||
} else if (ctx->in_buf->flush) {
|
||||
ctx->flush = Z_SYNC_FLUSH;
|
||||
|
||||
} else if (ctx->zstream.avail_in == 0) {
|
||||
/* ctx->flush == Z_NO_FLUSH */
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx)
|
||||
{
|
||||
ngx_http_gunzip_conf_t *conf;
|
||||
|
||||
if (ctx->zstream.avail_out) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
|
||||
|
||||
if (ctx->free) {
|
||||
ctx->out_buf = ctx->free->buf;
|
||||
ctx->free = ctx->free->next;
|
||||
|
||||
ctx->out_buf->flush = 0;
|
||||
|
||||
} else if (ctx->bufs < conf->bufs.num) {
|
||||
|
||||
ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
|
||||
if (ctx->out_buf == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;
|
||||
ctx->out_buf->recycled = 1;
|
||||
ctx->bufs++;
|
||||
|
||||
} else {
|
||||
ctx->nomem = 1;
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ctx->zstream.next_out = ctx->out_buf->pos;
|
||||
ctx->zstream.avail_out = conf->bufs.size;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
|
||||
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
|
||||
ctx->zstream.next_in, ctx->zstream.next_out,
|
||||
ctx->zstream.avail_in, ctx->zstream.avail_out,
|
||||
ctx->flush, ctx->redo);
|
||||
|
||||
rc = inflate(&ctx->zstream, ctx->flush);
|
||||
|
||||
if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"inflate() failed: %d, %d", ctx->flush, rc);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
|
||||
ctx->zstream.next_in, ctx->zstream.next_out,
|
||||
ctx->zstream.avail_in, ctx->zstream.avail_out,
|
||||
rc);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"gunzip in_buf:%p pos:%p",
|
||||
ctx->in_buf, ctx->in_buf->pos);
|
||||
|
||||
if (ctx->zstream.next_in) {
|
||||
ctx->in_buf->pos = ctx->zstream.next_in;
|
||||
|
||||
if (ctx->zstream.avail_in == 0) {
|
||||
ctx->zstream.next_in = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->out_buf->last = ctx->zstream.next_out;
|
||||
|
||||
if (ctx->zstream.avail_out == 0) {
|
||||
|
||||
/* zlib wants to output some more data */
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = ctx->out_buf;
|
||||
cl->next = NULL;
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
ctx->redo = 1;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->redo = 0;
|
||||
|
||||
if (ctx->flush == Z_SYNC_FLUSH) {
|
||||
|
||||
ctx->flush = Z_NO_FLUSH;
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = ctx->out_buf;
|
||||
|
||||
if (ngx_buf_size(b) == 0) {
|
||||
|
||||
b = ngx_calloc_buf(ctx->request->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
ctx->zstream.avail_out = 0;
|
||||
}
|
||||
|
||||
b->flush = 1;
|
||||
|
||||
cl->buf = b;
|
||||
cl->next = NULL;
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) {
|
||||
|
||||
if (rc != Z_STREAM_END) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"inflate() returned %d on response end", rc);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {
|
||||
|
||||
rc = inflateReset(&ctx->zstream);
|
||||
|
||||
if (rc != Z_OK) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"inflateReset() failed: %d", rc);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->redo = 1;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (ctx->in == NULL) {
|
||||
|
||||
b = ctx->out_buf;
|
||||
|
||||
if (ngx_buf_size(b) == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->zstream.avail_out = 0;
|
||||
|
||||
cl->buf = b;
|
||||
cl->next = NULL;
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
|
||||
ngx_http_gunzip_ctx_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"gunzip inflate end");
|
||||
|
||||
rc = inflateEnd(&ctx->zstream);
|
||||
|
||||
if (rc != Z_OK) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"inflateEnd() failed: %d", rc);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = ctx->out_buf;
|
||||
|
||||
if (ngx_buf_size(b) == 0) {
|
||||
|
||||
b = ngx_calloc_buf(ctx->request->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = b;
|
||||
cl->next = NULL;
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
b->last_buf = (r == r->main) ? 1 : 0;
|
||||
b->last_in_chain = 1;
|
||||
b->sync = 1;
|
||||
|
||||
ctx->done = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)
|
||||
{
|
||||
ngx_http_gunzip_ctx_t *ctx = opaque;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
|
||||
"gunzip alloc: n:%ud s:%ud",
|
||||
items, size);
|
||||
|
||||
return ngx_palloc(ctx->request->pool, items * size);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_gunzip_filter_free(void *opaque, void *address)
|
||||
{
|
||||
#if 0
|
||||
ngx_http_gunzip_ctx_t *ctx = opaque;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
|
||||
"gunzip free: %p", address);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_gunzip_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_gunzip_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->bufs.num = 0;
|
||||
*/
|
||||
|
||||
conf->enable = NGX_CONF_UNSET;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_gunzip_conf_t *prev = parent;
|
||||
ngx_http_gunzip_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_value(conf->enable, prev->enable, 0);
|
||||
|
||||
ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
|
||||
(128 * 1024) / ngx_pagesize, ngx_pagesize);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gunzip_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_gunzip_header_filter;
|
||||
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_gunzip_body_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,331 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_GZIP_STATIC_OFF 0
|
||||
#define NGX_HTTP_GZIP_STATIC_ON 1
|
||||
#define NGX_HTTP_GZIP_STATIC_ALWAYS 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t enable;
|
||||
} ngx_http_gzip_static_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);
|
||||
static void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_gzip_static[] = {
|
||||
{ ngx_string("off"), NGX_HTTP_GZIP_STATIC_OFF },
|
||||
{ ngx_string("on"), NGX_HTTP_GZIP_STATIC_ON },
|
||||
{ ngx_string("always"), NGX_HTTP_GZIP_STATIC_ALWAYS },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_gzip_static_commands[] = {
|
||||
|
||||
{ ngx_string("gzip_static"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_gzip_static_conf_t, enable),
|
||||
&ngx_http_gzip_static },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
ngx_http_module_t ngx_http_gzip_static_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_gzip_static_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_gzip_static_create_conf, /* create location configuration */
|
||||
ngx_http_gzip_static_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_gzip_static_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_gzip_static_module_ctx, /* module context */
|
||||
ngx_http_gzip_static_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_int_t
|
||||
ngx_http_gzip_static_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p;
|
||||
size_t root;
|
||||
ngx_str_t path;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t level;
|
||||
ngx_log_t *log;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t out;
|
||||
ngx_table_elt_t *h;
|
||||
ngx_open_file_info_t of;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_gzip_static_conf_t *gzcf;
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (r->uri.data[r->uri.len - 1] == '/') {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);
|
||||
|
||||
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
|
||||
rc = ngx_http_gzip_ok(r);
|
||||
|
||||
} else {
|
||||
/* always */
|
||||
rc = NGX_OK;
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (!clcf->gzip_vary && rc != NGX_OK) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
log = r->connection->log;
|
||||
|
||||
p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
|
||||
if (p == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
*p++ = '.';
|
||||
*p++ = 'g';
|
||||
*p++ = 'z';
|
||||
*p = '\0';
|
||||
|
||||
path.len = p - path.data;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
|
||||
"http filename: \"%s\"", path.data);
|
||||
|
||||
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
||||
|
||||
of.read_ahead = clcf->read_ahead;
|
||||
of.directio = clcf->directio;
|
||||
of.valid = clcf->open_file_cache_valid;
|
||||
of.min_uses = clcf->open_file_cache_min_uses;
|
||||
of.errors = clcf->open_file_cache_errors;
|
||||
of.events = clcf->open_file_cache_events;
|
||||
|
||||
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_open_cached_file(clcf->open_file_cache, &path, &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:
|
||||
|
||||
return NGX_DECLINED;
|
||||
|
||||
case NGX_EACCES:
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
case NGX_EMLINK:
|
||||
case NGX_ELOOP:
|
||||
#endif
|
||||
|
||||
level = NGX_LOG_ERR;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
level = NGX_LOG_CRIT;
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_log_error(level, log, of.err,
|
||||
"%s \"%s\" failed", of.failed, path.data);
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
|
||||
r->gzip_vary = 1;
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
|
||||
|
||||
if (of.is_dir) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
|
||||
|
||||
if (!of.is_file) {
|
||||
ngx_log_error(NGX_LOG_CRIT, log, 0,
|
||||
"\"%s\" is not a regular file", path.data);
|
||||
|
||||
return NGX_HTTP_NOT_FOUND;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
r->root_tested = !r->error_page;
|
||||
|
||||
rc = ngx_http_discard_request_body(r);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
log->action = "sending response to client";
|
||||
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
r->headers_out.content_length_n = of.size;
|
||||
r->headers_out.last_modified_time = of.mtime;
|
||||
|
||||
if (ngx_http_set_etag(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_set_content_type(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* we need to allocate all before the header would be sent */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
b->file_pos = 0;
|
||||
b->file_last = of.size;
|
||||
|
||||
b->in_file = b->file_last ? 1 : 0;
|
||||
b->last_buf = (r == r->main) ? 1 : 0;
|
||||
b->last_in_chain = 1;
|
||||
|
||||
b->file->fd = of.fd;
|
||||
b->file->name = path;
|
||||
b->file->log = log;
|
||||
b->file->directio = of.is_directio;
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
return ngx_http_output_filter(r, &out);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_gzip_static_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_gzip_static_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->enable = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_gzip_static_conf_t *prev = parent;
|
||||
ngx_http_gzip_static_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_uint_value(conf->enable, prev->enable,
|
||||
NGX_HTTP_GZIP_STATIC_OFF);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_gzip_static_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_gzip_static_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct ngx_http_header_val_s ngx_http_header_val_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
|
||||
ngx_http_header_val_t *hv, ngx_str_t *value);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_uint_t offset;
|
||||
ngx_http_set_header_pt handler;
|
||||
} ngx_http_set_header_t;
|
||||
|
||||
|
||||
struct ngx_http_header_val_s {
|
||||
ngx_http_complex_value_t value;
|
||||
ngx_str_t key;
|
||||
ngx_http_set_header_pt handler;
|
||||
ngx_uint_t offset;
|
||||
ngx_uint_t always; /* unsigned always:1 */
|
||||
};
|
||||
|
||||
|
||||
typedef enum {
|
||||
NGX_HTTP_EXPIRES_OFF,
|
||||
NGX_HTTP_EXPIRES_EPOCH,
|
||||
NGX_HTTP_EXPIRES_MAX,
|
||||
NGX_HTTP_EXPIRES_ACCESS,
|
||||
NGX_HTTP_EXPIRES_MODIFIED,
|
||||
NGX_HTTP_EXPIRES_DAILY,
|
||||
NGX_HTTP_EXPIRES_UNSET
|
||||
} ngx_http_expires_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_expires_t expires;
|
||||
time_t expires_time;
|
||||
ngx_http_complex_value_t *expires_value;
|
||||
ngx_array_t *headers;
|
||||
} ngx_http_headers_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
|
||||
ngx_http_headers_conf_t *conf);
|
||||
static ngx_int_t ngx_http_parse_expires(ngx_str_t *value,
|
||||
ngx_http_expires_t *expires, time_t *expires_time, char **err);
|
||||
static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
|
||||
ngx_http_header_val_t *hv, ngx_str_t *value);
|
||||
static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
|
||||
ngx_http_header_val_t *hv, ngx_str_t *value);
|
||||
static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
|
||||
ngx_http_header_val_t *hv, ngx_str_t *value);
|
||||
static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
|
||||
ngx_http_header_val_t *hv, ngx_str_t *value);
|
||||
|
||||
static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
|
||||
static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_http_set_header_t ngx_http_set_headers[] = {
|
||||
|
||||
{ ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
|
||||
|
||||
{ ngx_string("Last-Modified"),
|
||||
offsetof(ngx_http_headers_out_t, last_modified),
|
||||
ngx_http_set_last_modified },
|
||||
|
||||
{ ngx_string("ETag"),
|
||||
offsetof(ngx_http_headers_out_t, etag),
|
||||
ngx_http_set_response_header },
|
||||
|
||||
{ ngx_null_string, 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_headers_filter_commands[] = {
|
||||
|
||||
{ ngx_string("expires"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||
|NGX_CONF_TAKE12,
|
||||
ngx_http_headers_expires,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL},
|
||||
|
||||
{ ngx_string("add_header"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||
|NGX_CONF_TAKE23,
|
||||
ngx_http_headers_add,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL},
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_headers_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_headers_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_headers_create_conf, /* create location configuration */
|
||||
ngx_http_headers_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_headers_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_headers_filter_module_ctx, /* module context */
|
||||
ngx_http_headers_filter_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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_headers_filter(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_str_t value;
|
||||
ngx_uint_t i, safe_status;
|
||||
ngx_http_header_val_t *h;
|
||||
ngx_http_headers_conf_t *conf;
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
|
||||
|
||||
if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
|
||||
|| r != r->main)
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
switch (r->headers_out.status) {
|
||||
|
||||
case NGX_HTTP_OK:
|
||||
case NGX_HTTP_CREATED:
|
||||
case NGX_HTTP_NO_CONTENT:
|
||||
case NGX_HTTP_PARTIAL_CONTENT:
|
||||
case NGX_HTTP_MOVED_PERMANENTLY:
|
||||
case NGX_HTTP_MOVED_TEMPORARILY:
|
||||
case NGX_HTTP_SEE_OTHER:
|
||||
case NGX_HTTP_NOT_MODIFIED:
|
||||
case NGX_HTTP_TEMPORARY_REDIRECT:
|
||||
safe_status = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
safe_status = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
|
||||
if (ngx_http_set_expires(r, conf) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->headers) {
|
||||
h = conf->headers->elts;
|
||||
for (i = 0; i < conf->headers->nelts; i++) {
|
||||
|
||||
if (!safe_status && !h[i].always) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (h[i].handler(r, &h[i], &value) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
|
||||
{
|
||||
char *err;
|
||||
size_t len;
|
||||
time_t now, expires_time, max_age;
|
||||
ngx_str_t value;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t i;
|
||||
ngx_table_elt_t *e, *cc, **ccp;
|
||||
ngx_http_expires_t expires;
|
||||
|
||||
expires = conf->expires;
|
||||
expires_time = conf->expires_time;
|
||||
|
||||
if (conf->expires_value != NULL) {
|
||||
|
||||
if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (expires == NGX_HTTP_EXPIRES_OFF) {
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
e = r->headers_out.expires;
|
||||
|
||||
if (e == NULL) {
|
||||
|
||||
e = ngx_list_push(&r->headers_out.headers);
|
||||
if (e == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.expires = e;
|
||||
|
||||
e->hash = 1;
|
||||
ngx_str_set(&e->key, "Expires");
|
||||
}
|
||||
|
||||
len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
|
||||
e->value.len = len - 1;
|
||||
|
||||
ccp = r->headers_out.cache_control.elts;
|
||||
|
||||
if (ccp == NULL) {
|
||||
|
||||
if (ngx_array_init(&r->headers_out.cache_control, r->pool,
|
||||
1, sizeof(ngx_table_elt_t *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ccp = ngx_array_push(&r->headers_out.cache_control);
|
||||
if (ccp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cc = ngx_list_push(&r->headers_out.headers);
|
||||
if (cc == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cc->hash = 1;
|
||||
ngx_str_set(&cc->key, "Cache-Control");
|
||||
*ccp = cc;
|
||||
|
||||
} else {
|
||||
for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
|
||||
ccp[i]->hash = 0;
|
||||
}
|
||||
|
||||
cc = ccp[0];
|
||||
}
|
||||
|
||||
if (expires == NGX_HTTP_EXPIRES_EPOCH) {
|
||||
e->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
|
||||
ngx_str_set(&cc->value, "no-cache");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (expires == NGX_HTTP_EXPIRES_MAX) {
|
||||
e->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
|
||||
/* 10 years */
|
||||
ngx_str_set(&cc->value, "max-age=315360000");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
e->value.data = ngx_pnalloc(r->pool, len);
|
||||
if (e->value.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {
|
||||
ngx_memcpy(e->value.data, ngx_cached_http_time.data,
|
||||
ngx_cached_http_time.len + 1);
|
||||
ngx_str_set(&cc->value, "max-age=0");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
if (expires == NGX_HTTP_EXPIRES_DAILY) {
|
||||
expires_time = ngx_next_time(expires_time);
|
||||
max_age = expires_time - now;
|
||||
|
||||
} else if (expires == NGX_HTTP_EXPIRES_ACCESS
|
||||
|| r->headers_out.last_modified_time == -1)
|
||||
{
|
||||
max_age = expires_time;
|
||||
expires_time += now;
|
||||
|
||||
} else {
|
||||
expires_time += r->headers_out.last_modified_time;
|
||||
max_age = expires_time - now;
|
||||
}
|
||||
|
||||
ngx_http_time(e->value.data, expires_time);
|
||||
|
||||
if (conf->expires_time < 0 || max_age < 0) {
|
||||
ngx_str_set(&cc->value, "no-cache");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
cc->value.data = ngx_pnalloc(r->pool,
|
||||
sizeof("max-age=") + NGX_TIME_T_LEN + 1);
|
||||
if (cc->value.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
|
||||
- cc->value.data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,
|
||||
time_t *expires_time, char **err)
|
||||
{
|
||||
ngx_uint_t minus;
|
||||
|
||||
if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {
|
||||
|
||||
if (value->len == 5 && ngx_strncmp(value->data, "epoch", 5) == 0) {
|
||||
*expires = NGX_HTTP_EXPIRES_EPOCH;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (value->len == 3 && ngx_strncmp(value->data, "max", 3) == 0) {
|
||||
*expires = NGX_HTTP_EXPIRES_MAX;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (value->len == 3 && ngx_strncmp(value->data, "off", 3) == 0) {
|
||||
*expires = NGX_HTTP_EXPIRES_OFF;
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (value->len && value->data[0] == '@') {
|
||||
value->data++;
|
||||
value->len--;
|
||||
minus = 0;
|
||||
|
||||
if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {
|
||||
*err = "daily time cannot be used with \"modified\" parameter";
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*expires = NGX_HTTP_EXPIRES_DAILY;
|
||||
|
||||
} else if (value->len && value->data[0] == '+') {
|
||||
value->data++;
|
||||
value->len--;
|
||||
minus = 0;
|
||||
|
||||
} else if (value->len && value->data[0] == '-') {
|
||||
value->data++;
|
||||
value->len--;
|
||||
minus = 1;
|
||||
|
||||
} else {
|
||||
minus = 0;
|
||||
}
|
||||
|
||||
*expires_time = ngx_parse_time(value, 1);
|
||||
|
||||
if (*expires_time == (time_t) NGX_ERROR) {
|
||||
*err = "invalid value";
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (*expires == NGX_HTTP_EXPIRES_DAILY
|
||||
&& *expires_time > 24 * 60 * 60)
|
||||
{
|
||||
*err = "daily time value must be less than 24 hours";
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (minus) {
|
||||
*expires_time = - *expires_time;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
|
||||
ngx_str_t *value)
|
||||
{
|
||||
ngx_table_elt_t *h;
|
||||
|
||||
if (value->len) {
|
||||
h = ngx_list_push(&r->headers_out.headers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h->hash = 1;
|
||||
h->key = hv->key;
|
||||
h->value = *value;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
|
||||
ngx_str_t *value)
|
||||
{
|
||||
ngx_table_elt_t *cc, **ccp;
|
||||
|
||||
if (value->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ccp = r->headers_out.cache_control.elts;
|
||||
|
||||
if (ccp == NULL) {
|
||||
|
||||
if (ngx_array_init(&r->headers_out.cache_control, r->pool,
|
||||
1, sizeof(ngx_table_elt_t *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ccp = ngx_array_push(&r->headers_out.cache_control);
|
||||
if (ccp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cc = ngx_list_push(&r->headers_out.headers);
|
||||
if (cc == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cc->hash = 1;
|
||||
ngx_str_set(&cc->key, "Cache-Control");
|
||||
cc->value = *value;
|
||||
|
||||
*ccp = cc;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
|
||||
ngx_str_t *value)
|
||||
{
|
||||
if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.last_modified_time =
|
||||
(value->len) ? ngx_http_parse_time(value->data, value->len) : -1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
|
||||
ngx_str_t *value)
|
||||
{
|
||||
ngx_table_elt_t *h, **old;
|
||||
|
||||
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
|
||||
|
||||
if (value->len == 0) {
|
||||
if (*old) {
|
||||
(*old)->hash = 0;
|
||||
*old = NULL;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (*old) {
|
||||
h = *old;
|
||||
|
||||
} else {
|
||||
h = ngx_list_push(&r->headers_out.headers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*old = h;
|
||||
}
|
||||
|
||||
h->hash = 1;
|
||||
h->key = hv->key;
|
||||
h->value = *value;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_headers_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_headers_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->headers = NULL;
|
||||
* conf->expires_time = 0;
|
||||
* conf->expires_value = NULL;
|
||||
*/
|
||||
|
||||
conf->expires = NGX_HTTP_EXPIRES_UNSET;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_headers_conf_t *prev = parent;
|
||||
ngx_http_headers_conf_t *conf = child;
|
||||
|
||||
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
|
||||
conf->expires = prev->expires;
|
||||
conf->expires_time = prev->expires_time;
|
||||
conf->expires_value = prev->expires_value;
|
||||
|
||||
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
|
||||
conf->expires = NGX_HTTP_EXPIRES_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->headers == NULL) {
|
||||
conf->headers = prev->headers;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_headers_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_headers_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_headers_conf_t *hcf = conf;
|
||||
|
||||
char *err;
|
||||
ngx_str_t *value;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t n;
|
||||
ngx_http_complex_value_t cv;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (cf->args->nelts == 2) {
|
||||
|
||||
hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
|
||||
|
||||
n = 1;
|
||||
|
||||
} else { /* cf->args->nelts == 3 */
|
||||
|
||||
if (ngx_strcmp(value[1].data, "modified") != 0) {
|
||||
return "invalid value";
|
||||
}
|
||||
|
||||
hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
|
||||
|
||||
n = 2;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[n];
|
||||
ccv.complex_value = &cv;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cv.lengths != NULL) {
|
||||
|
||||
hcf->expires_value = ngx_palloc(cf->pool,
|
||||
sizeof(ngx_http_complex_value_t));
|
||||
if (hcf->expires_value == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*hcf->expires_value = cv;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,
|
||||
&err);
|
||||
if (rc != NGX_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_headers_conf_t *hcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_uint_t i;
|
||||
ngx_http_header_val_t *hv;
|
||||
ngx_http_set_header_t *set;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (hcf->headers == NULL) {
|
||||
hcf->headers = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_http_header_val_t));
|
||||
if (hcf->headers == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
hv = ngx_array_push(hcf->headers);
|
||||
if (hv == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
hv->key = value[1];
|
||||
hv->handler = ngx_http_add_header;
|
||||
hv->offset = 0;
|
||||
hv->always = 0;
|
||||
|
||||
set = ngx_http_set_headers;
|
||||
for (i = 0; set[i].name.len; i++) {
|
||||
if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hv->offset = set[i].offset;
|
||||
hv->handler = set[i].handler;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (value[2].len == 0) {
|
||||
ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[2];
|
||||
ccv.complex_value = &hv->value;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[3].data, "always") != 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[3]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
hv->always = 1;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,540 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_array_t *lengths;
|
||||
ngx_array_t *values;
|
||||
} ngx_http_index_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *indices; /* array of ngx_http_index_t */
|
||||
size_t max_index_len;
|
||||
} ngx_http_index_loc_conf_t;
|
||||
|
||||
|
||||
#define NGX_HTTP_DEFAULT_INDEX "index.html"
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
|
||||
ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
|
||||
static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
|
||||
ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);
|
||||
|
||||
static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
|
||||
static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_index_commands[] = {
|
||||
|
||||
{ ngx_string("index"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_index_set_index,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_index_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_index_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_index_create_loc_conf, /* create location configuration */
|
||||
ngx_http_index_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_index_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_index_module_ctx, /* module context */
|
||||
ngx_http_index_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
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Try to open/test the first index file before the test of directory
|
||||
* existence because valid requests should prevail over invalid ones.
|
||||
* If open()/stat() of a file will fail then stat() of a directory
|
||||
* should be faster because kernel may have already cached some data.
|
||||
* Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
|
||||
* Unix has ENOTDIR error; however, it's less helpful than Win32's one:
|
||||
* it only indicates that path points to a regular file, not a directory.
|
||||
*/
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_index_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p, *name;
|
||||
size_t len, root, reserve, allocated;
|
||||
ngx_int_t rc;
|
||||
ngx_str_t path, uri;
|
||||
ngx_uint_t i, dir_tested;
|
||||
ngx_http_index_t *index;
|
||||
ngx_open_file_info_t of;
|
||||
ngx_http_script_code_pt code;
|
||||
ngx_http_script_engine_t e;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_index_loc_conf_t *ilcf;
|
||||
ngx_http_script_len_code_pt lcode;
|
||||
|
||||
if (r->uri.data[r->uri.len - 1] != '/') {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
allocated = 0;
|
||||
root = 0;
|
||||
dir_tested = 0;
|
||||
name = NULL;
|
||||
/* suppress MSVC warning */
|
||||
path.data = NULL;
|
||||
|
||||
index = ilcf->indices->elts;
|
||||
for (i = 0; i < ilcf->indices->nelts; i++) {
|
||||
|
||||
if (index[i].lengths == NULL) {
|
||||
|
||||
if (index[i].name.data[0] == '/') {
|
||||
return ngx_http_internal_redirect(r, &index[i].name, &r->args);
|
||||
}
|
||||
|
||||
reserve = ilcf->max_index_len;
|
||||
len = index[i].name.len;
|
||||
|
||||
} else {
|
||||
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
|
||||
|
||||
e.ip = index[i].lengths->elts;
|
||||
e.request = r;
|
||||
e.flushed = 1;
|
||||
|
||||
/* 1 is for terminating '\0' as in static names */
|
||||
len = 1;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
lcode = *(ngx_http_script_len_code_pt *) e.ip;
|
||||
len += lcode(&e);
|
||||
}
|
||||
|
||||
/* 16 bytes are preallocation */
|
||||
|
||||
reserve = len + 16;
|
||||
}
|
||||
|
||||
if (reserve > allocated) {
|
||||
|
||||
name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
|
||||
if (name == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
allocated = path.data + path.len - name;
|
||||
}
|
||||
|
||||
if (index[i].values == NULL) {
|
||||
|
||||
/* index[i].name.len includes the terminating '\0' */
|
||||
|
||||
ngx_memcpy(name, index[i].name.data, index[i].name.len);
|
||||
|
||||
path.len = (name + index[i].name.len - 1) - path.data;
|
||||
|
||||
} else {
|
||||
e.ip = index[i].values->elts;
|
||||
e.pos = name;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_http_script_code_pt *) e.ip;
|
||||
code((ngx_http_script_engine_t *) &e);
|
||||
}
|
||||
|
||||
if (*name == '/') {
|
||||
uri.len = len - 1;
|
||||
uri.data = name;
|
||||
return ngx_http_internal_redirect(r, &uri, &r->args);
|
||||
}
|
||||
|
||||
path.len = e.pos - path.data;
|
||||
|
||||
*e.pos = '\0';
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"open index \"%V\"", &path);
|
||||
|
||||
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
||||
|
||||
of.read_ahead = clcf->read_ahead;
|
||||
of.directio = clcf->directio;
|
||||
of.valid = clcf->open_file_cache_valid;
|
||||
of.min_uses = clcf->open_file_cache_min_uses;
|
||||
of.test_only = 1;
|
||||
of.errors = clcf->open_file_cache_errors;
|
||||
of.events = clcf->open_file_cache_events;
|
||||
|
||||
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
|
||||
"%s \"%s\" failed", of.failed, path.data);
|
||||
|
||||
if (of.err == 0) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
if (of.err == NGX_EMLINK
|
||||
|| of.err == NGX_ELOOP)
|
||||
{
|
||||
return NGX_HTTP_FORBIDDEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (of.err == NGX_ENOTDIR
|
||||
|| of.err == NGX_ENAMETOOLONG
|
||||
|| of.err == NGX_EACCES)
|
||||
{
|
||||
return ngx_http_index_error(r, clcf, path.data, of.err);
|
||||
}
|
||||
|
||||
if (!dir_tested) {
|
||||
rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
dir_tested = 1;
|
||||
}
|
||||
|
||||
if (of.err == NGX_ENOENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
|
||||
"%s \"%s\" failed", of.failed, path.data);
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
uri.len = r->uri.len + len - 1;
|
||||
|
||||
if (!clcf->alias) {
|
||||
uri.data = path.data + root;
|
||||
|
||||
} else {
|
||||
uri.data = ngx_pnalloc(r->pool, uri.len);
|
||||
if (uri.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_copy(uri.data, r->uri.data, r->uri.len);
|
||||
ngx_memcpy(p, name, len - 1);
|
||||
}
|
||||
|
||||
return ngx_http_internal_redirect(r, &uri, &r->args);
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
|
||||
u_char *path, u_char *last)
|
||||
{
|
||||
u_char c;
|
||||
ngx_str_t dir;
|
||||
ngx_open_file_info_t of;
|
||||
|
||||
c = *last;
|
||||
if (c != '/' || path == last) {
|
||||
/* "alias" without trailing slash */
|
||||
c = *(++last);
|
||||
}
|
||||
*last = '\0';
|
||||
|
||||
dir.len = last - path;
|
||||
dir.data = path;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http index check dir: \"%V\"", &dir);
|
||||
|
||||
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
||||
|
||||
of.test_dir = 1;
|
||||
of.test_only = 1;
|
||||
of.valid = clcf->open_file_cache_valid;
|
||||
of.errors = clcf->open_file_cache_errors;
|
||||
|
||||
if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
|
||||
!= NGX_OK)
|
||||
{
|
||||
if (of.err) {
|
||||
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
if (of.err == NGX_EMLINK
|
||||
|| of.err == NGX_ELOOP)
|
||||
{
|
||||
return NGX_HTTP_FORBIDDEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (of.err == NGX_ENOENT) {
|
||||
*last = c;
|
||||
return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
|
||||
}
|
||||
|
||||
if (of.err == NGX_EACCES) {
|
||||
|
||||
*last = c;
|
||||
|
||||
/*
|
||||
* ngx_http_index_test_dir() is called after the first index
|
||||
* file testing has returned an error distinct from NGX_EACCES.
|
||||
* This means that directory searching is allowed.
|
||||
*/
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
|
||||
"%s \"%s\" failed", of.failed, dir.data);
|
||||
}
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
*last = c;
|
||||
|
||||
if (of.is_dir) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"\"%s\" is not a directory", dir.data);
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
|
||||
u_char *file, ngx_err_t err)
|
||||
{
|
||||
if (err == NGX_EACCES) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
|
||||
"\"%s\" is forbidden", file);
|
||||
|
||||
return NGX_HTTP_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (clcf->log_not_found) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
|
||||
"\"%s\" is not found", file);
|
||||
}
|
||||
|
||||
return NGX_HTTP_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_index_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_index_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->indices = NULL;
|
||||
conf->max_index_len = 0;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_index_loc_conf_t *prev = parent;
|
||||
ngx_http_index_loc_conf_t *conf = child;
|
||||
|
||||
ngx_http_index_t *index;
|
||||
|
||||
if (conf->indices == NULL) {
|
||||
conf->indices = prev->indices;
|
||||
conf->max_index_len = prev->max_index_len;
|
||||
}
|
||||
|
||||
if (conf->indices == NULL) {
|
||||
conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
|
||||
if (conf->indices == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
index = ngx_array_push(conf->indices);
|
||||
if (index == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
|
||||
index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
|
||||
index->lengths = NULL;
|
||||
index->values = NULL;
|
||||
|
||||
conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_index_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_index_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
/* TODO: warn about duplicate indices */
|
||||
|
||||
static char *
|
||||
ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_index_loc_conf_t *ilcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_uint_t i, n;
|
||||
ngx_http_index_t *index;
|
||||
ngx_http_script_compile_t sc;
|
||||
|
||||
if (ilcf->indices == NULL) {
|
||||
ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
|
||||
if (ilcf->indices == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"only the last index in \"index\" directive "
|
||||
"should be absolute");
|
||||
}
|
||||
|
||||
if (value[i].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"index \"%V\" in \"index\" directive is invalid",
|
||||
&value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
index = ngx_array_push(ilcf->indices);
|
||||
if (index == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
index->name.len = value[i].len;
|
||||
index->name.data = value[i].data;
|
||||
index->lengths = NULL;
|
||||
index->values = NULL;
|
||||
|
||||
n = ngx_http_script_variables_count(&value[i]);
|
||||
|
||||
if (n == 0) {
|
||||
if (ilcf->max_index_len < index->name.len) {
|
||||
ilcf->max_index_len = index->name.len;
|
||||
}
|
||||
|
||||
if (index->name.data[0] == '/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* include the terminating '\0' to the length to use ngx_memcpy() */
|
||||
index->name.len++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
|
||||
|
||||
sc.cf = cf;
|
||||
sc.source = &value[i];
|
||||
sc.lengths = &index->lengths;
|
||||
sc.values = &index->values;
|
||||
sc.variables = n;
|
||||
sc.complete_lengths = 1;
|
||||
sc.complete_values = 1;
|
||||
|
||||
if (ngx_http_script_compile(&sc) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char color;
|
||||
u_char len;
|
||||
u_short conn;
|
||||
u_char data[1];
|
||||
} ngx_http_limit_conn_node_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_rbtree_node_t *node;
|
||||
} ngx_http_limit_conn_cleanup_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_t *rbtree;
|
||||
ngx_http_complex_value_t key;
|
||||
} ngx_http_limit_conn_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_uint_t conn;
|
||||
} ngx_http_limit_conn_limit_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t limits;
|
||||
ngx_uint_t log_level;
|
||||
ngx_uint_t status_code;
|
||||
} ngx_http_limit_conn_conf_t;
|
||||
|
||||
|
||||
static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
|
||||
ngx_str_t *key, uint32_t hash);
|
||||
static void ngx_http_limit_conn_cleanup(void *data);
|
||||
static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
|
||||
|
||||
static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = {
|
||||
{ ngx_string("info"), NGX_LOG_INFO },
|
||||
{ ngx_string("notice"), NGX_LOG_NOTICE },
|
||||
{ ngx_string("warn"), NGX_LOG_WARN },
|
||||
{ ngx_string("error"), NGX_LOG_ERR },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_num_bounds_t ngx_http_limit_conn_status_bounds = {
|
||||
ngx_conf_check_num_bounds, 400, 599
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_limit_conn_commands[] = {
|
||||
|
||||
{ ngx_string("limit_conn_zone"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
|
||||
ngx_http_limit_conn_zone,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_conn"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
|
||||
ngx_http_limit_conn,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_conn_log_level"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_limit_conn_conf_t, log_level),
|
||||
&ngx_http_limit_conn_log_levels },
|
||||
|
||||
{ ngx_string("limit_conn_status"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_limit_conn_conf_t, status_code),
|
||||
&ngx_http_limit_conn_status_bounds },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_limit_conn_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_limit_conn_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_limit_conn_create_conf, /* create location configuration */
|
||||
ngx_http_limit_conn_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_limit_conn_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_limit_conn_module_ctx, /* module context */
|
||||
ngx_http_limit_conn_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_int_t
|
||||
ngx_http_limit_conn_handler(ngx_http_request_t *r)
|
||||
{
|
||||
size_t n;
|
||||
uint32_t hash;
|
||||
ngx_str_t key;
|
||||
ngx_uint_t i;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_pool_cleanup_t *cln;
|
||||
ngx_http_limit_conn_ctx_t *ctx;
|
||||
ngx_http_limit_conn_node_t *lc;
|
||||
ngx_http_limit_conn_conf_t *lccf;
|
||||
ngx_http_limit_conn_limit_t *limits;
|
||||
ngx_http_limit_conn_cleanup_t *lccln;
|
||||
|
||||
if (r->main->limit_conn_set) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
|
||||
limits = lccf->limits.elts;
|
||||
|
||||
for (i = 0; i < lccf->limits.nelts; i++) {
|
||||
ctx = limits[i].shm_zone->data;
|
||||
|
||||
if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (key.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.len > 255) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"the value of the \"%V\" key "
|
||||
"is more than 255 bytes: \"%V\"",
|
||||
&ctx->key.value, &key);
|
||||
continue;
|
||||
}
|
||||
|
||||
r->main->limit_conn_set = 1;
|
||||
|
||||
hash = ngx_crc32_short(key.data, key.len);
|
||||
|
||||
shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash);
|
||||
|
||||
if (node == NULL) {
|
||||
|
||||
n = offsetof(ngx_rbtree_node_t, color)
|
||||
+ offsetof(ngx_http_limit_conn_node_t, data)
|
||||
+ key.len;
|
||||
|
||||
node = ngx_slab_alloc_locked(shpool, n);
|
||||
|
||||
if (node == NULL) {
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
ngx_http_limit_conn_cleanup_all(r->pool);
|
||||
return lccf->status_code;
|
||||
}
|
||||
|
||||
lc = (ngx_http_limit_conn_node_t *) &node->color;
|
||||
|
||||
node->key = hash;
|
||||
lc->len = (u_char) key.len;
|
||||
lc->conn = 1;
|
||||
ngx_memcpy(lc->data, key.data, key.len);
|
||||
|
||||
ngx_rbtree_insert(ctx->rbtree, node);
|
||||
|
||||
} else {
|
||||
|
||||
lc = (ngx_http_limit_conn_node_t *) &node->color;
|
||||
|
||||
if ((ngx_uint_t) lc->conn >= limits[i].conn) {
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
ngx_log_error(lccf->log_level, r->connection->log, 0,
|
||||
"limiting connections by zone \"%V\"",
|
||||
&limits[i].shm_zone->shm.name);
|
||||
|
||||
ngx_http_limit_conn_cleanup_all(r->pool);
|
||||
return lccf->status_code;
|
||||
}
|
||||
|
||||
lc->conn++;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"limit conn: %08XD %d", node->key, lc->conn);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
cln = ngx_pool_cleanup_add(r->pool,
|
||||
sizeof(ngx_http_limit_conn_cleanup_t));
|
||||
if (cln == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
cln->handler = ngx_http_limit_conn_cleanup;
|
||||
lccln = cln->data;
|
||||
|
||||
lccln->shm_zone = limits[i].shm_zone;
|
||||
lccln->node = node;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
|
||||
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
||||
{
|
||||
ngx_rbtree_node_t **p;
|
||||
ngx_http_limit_conn_node_t *lcn, *lcnt;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (node->key < temp->key) {
|
||||
|
||||
p = &temp->left;
|
||||
|
||||
} else if (node->key > temp->key) {
|
||||
|
||||
p = &temp->right;
|
||||
|
||||
} else { /* node->key == temp->key */
|
||||
|
||||
lcn = (ngx_http_limit_conn_node_t *) &node->color;
|
||||
lcnt = (ngx_http_limit_conn_node_t *) &temp->color;
|
||||
|
||||
p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
|
||||
? &temp->left : &temp->right;
|
||||
}
|
||||
|
||||
if (*p == sentinel) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp = *p;
|
||||
}
|
||||
|
||||
*p = node;
|
||||
node->parent = temp;
|
||||
node->left = sentinel;
|
||||
node->right = sentinel;
|
||||
ngx_rbt_red(node);
|
||||
}
|
||||
|
||||
|
||||
static ngx_rbtree_node_t *
|
||||
ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_http_limit_conn_node_t *lcn;
|
||||
|
||||
node = rbtree->root;
|
||||
sentinel = rbtree->sentinel;
|
||||
|
||||
while (node != sentinel) {
|
||||
|
||||
if (hash < node->key) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash > node->key) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* hash == node->key */
|
||||
|
||||
lcn = (ngx_http_limit_conn_node_t *) &node->color;
|
||||
|
||||
rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
|
||||
|
||||
if (rc == 0) {
|
||||
return node;
|
||||
}
|
||||
|
||||
node = (rc < 0) ? node->left : node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_limit_conn_cleanup(void *data)
|
||||
{
|
||||
ngx_http_limit_conn_cleanup_t *lccln = data;
|
||||
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_http_limit_conn_ctx_t *ctx;
|
||||
ngx_http_limit_conn_node_t *lc;
|
||||
|
||||
ctx = lccln->shm_zone->data;
|
||||
shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
|
||||
node = lccln->node;
|
||||
lc = (ngx_http_limit_conn_node_t *) &node->color;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
|
||||
"limit conn cleanup: %08XD %d", node->key, lc->conn);
|
||||
|
||||
lc->conn--;
|
||||
|
||||
if (lc->conn == 0) {
|
||||
ngx_rbtree_delete(ctx->rbtree, node);
|
||||
ngx_slab_free_locked(shpool, node);
|
||||
}
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
}
|
||||
|
||||
|
||||
static ngx_inline void
|
||||
ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
|
||||
{
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
cln = pool->cleanup;
|
||||
|
||||
while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
|
||||
ngx_http_limit_conn_cleanup(cln->data);
|
||||
cln = cln->next;
|
||||
}
|
||||
|
||||
pool->cleanup = cln;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
ngx_http_limit_conn_ctx_t *octx = data;
|
||||
|
||||
size_t len;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *sentinel;
|
||||
ngx_http_limit_conn_ctx_t *ctx;
|
||||
|
||||
ctx = shm_zone->data;
|
||||
|
||||
if (octx) {
|
||||
if (ctx->key.value.len != octx->key.value.len
|
||||
|| ngx_strncmp(ctx->key.value.data, octx->key.value.data,
|
||||
ctx->key.value.len)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
|
||||
"limit_conn_zone \"%V\" uses the \"%V\" key "
|
||||
"while previously it used the \"%V\" key",
|
||||
&shm_zone->shm.name, &ctx->key.value,
|
||||
&octx->key.value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->rbtree = octx->rbtree;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
|
||||
if (shm_zone->shm.exists) {
|
||||
ctx->rbtree = shpool->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
|
||||
if (ctx->rbtree == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
shpool->data = ctx->rbtree;
|
||||
|
||||
sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
|
||||
if (sentinel == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_rbtree_init(ctx->rbtree, sentinel,
|
||||
ngx_http_limit_conn_rbtree_insert_value);
|
||||
|
||||
len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
|
||||
|
||||
shpool->log_ctx = ngx_slab_alloc(shpool, len);
|
||||
if (shpool->log_ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
|
||||
&shm_zone->shm.name);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_limit_conn_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->limits.elts = NULL;
|
||||
*/
|
||||
|
||||
conf->log_level = NGX_CONF_UNSET_UINT;
|
||||
conf->status_code = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_limit_conn_conf_t *prev = parent;
|
||||
ngx_http_limit_conn_conf_t *conf = child;
|
||||
|
||||
if (conf->limits.elts == NULL) {
|
||||
conf->limits = prev->limits;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
|
||||
ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
|
||||
NGX_HTTP_SERVICE_UNAVAILABLE);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
u_char *p;
|
||||
ssize_t size;
|
||||
ngx_str_t *value, name, s;
|
||||
ngx_uint_t i;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_http_limit_conn_ctx_t *ctx;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &ctx->key;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
name.len = 0;
|
||||
|
||||
for (i = 2; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
|
||||
|
||||
name.data = value[i].data + 5;
|
||||
|
||||
p = (u_char *) ngx_strchr(name.data, ':');
|
||||
|
||||
if (p == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len = p - name.data;
|
||||
|
||||
s.data = p + 1;
|
||||
s.len = value[i].data + value[i].len - s.data;
|
||||
|
||||
size = ngx_parse_size(&s);
|
||||
|
||||
if (size == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (size < (ssize_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"zone \"%V\" is too small", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (name.len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"%V\" must have \"zone\" parameter",
|
||||
&cmd->name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &name, size,
|
||||
&ngx_http_limit_conn_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (shm_zone->data) {
|
||||
ctx = shm_zone->data;
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%V \"%V\" is already bound to key \"%V\"",
|
||||
&cmd->name, &name, &ctx->key.value);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
shm_zone->init = ngx_http_limit_conn_init_zone;
|
||||
shm_zone->data = ctx;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_http_limit_conn_conf_t *lccf = conf;
|
||||
ngx_http_limit_conn_limit_t *limit, *limits;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t i;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
|
||||
&ngx_http_limit_conn_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limits = lccf->limits.elts;
|
||||
|
||||
if (limits == NULL) {
|
||||
if (ngx_array_init(&lccf->limits, cf->pool, 1,
|
||||
sizeof(ngx_http_limit_conn_limit_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lccf->limits.nelts; i++) {
|
||||
if (shm_zone == limits[i].shm_zone) {
|
||||
return "is duplicate";
|
||||
}
|
||||
}
|
||||
|
||||
n = ngx_atoi(value[2].data, value[2].len);
|
||||
if (n <= 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid number of connections \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (n > 65535) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"connection limit must be less 65536");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limit = ngx_array_push(&lccf->limits);
|
||||
if (limit == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limit->conn = n;
|
||||
limit->shm_zone = shm_zone;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_limit_conn_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_limit_conn_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,975 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char color;
|
||||
u_char dummy;
|
||||
u_short len;
|
||||
ngx_queue_t queue;
|
||||
ngx_msec_t last;
|
||||
/* integer value, 1 corresponds to 0.001 r/s */
|
||||
ngx_uint_t excess;
|
||||
ngx_uint_t count;
|
||||
u_char data[1];
|
||||
} ngx_http_limit_req_node_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_t rbtree;
|
||||
ngx_rbtree_node_t sentinel;
|
||||
ngx_queue_t queue;
|
||||
} ngx_http_limit_req_shctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_limit_req_shctx_t *sh;
|
||||
ngx_slab_pool_t *shpool;
|
||||
/* integer value, 1 corresponds to 0.001 r/s */
|
||||
ngx_uint_t rate;
|
||||
ngx_http_complex_value_t key;
|
||||
ngx_http_limit_req_node_t *node;
|
||||
} ngx_http_limit_req_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
/* integer value, 1 corresponds to 0.001 r/s */
|
||||
ngx_uint_t burst;
|
||||
ngx_uint_t nodelay; /* unsigned nodelay:1 */
|
||||
} ngx_http_limit_req_limit_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t limits;
|
||||
ngx_uint_t limit_log_level;
|
||||
ngx_uint_t delay_log_level;
|
||||
ngx_uint_t status_code;
|
||||
} ngx_http_limit_req_conf_t;
|
||||
|
||||
|
||||
static void ngx_http_limit_req_delay(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
|
||||
ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);
|
||||
static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
|
||||
ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
|
||||
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
|
||||
ngx_uint_t n);
|
||||
|
||||
static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_limit_req_log_levels[] = {
|
||||
{ ngx_string("info"), NGX_LOG_INFO },
|
||||
{ ngx_string("notice"), NGX_LOG_NOTICE },
|
||||
{ ngx_string("warn"), NGX_LOG_WARN },
|
||||
{ ngx_string("error"), NGX_LOG_ERR },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
|
||||
ngx_conf_check_num_bounds, 400, 599
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_limit_req_commands[] = {
|
||||
|
||||
{ ngx_string("limit_req_zone"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
|
||||
ngx_http_limit_req_zone,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_req"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
|
||||
ngx_http_limit_req,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_req_log_level"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_limit_req_conf_t, limit_log_level),
|
||||
&ngx_http_limit_req_log_levels },
|
||||
|
||||
{ ngx_string("limit_req_status"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_limit_req_conf_t, status_code),
|
||||
&ngx_http_limit_req_status_bounds },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_limit_req_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_limit_req_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_limit_req_create_conf, /* create location configuration */
|
||||
ngx_http_limit_req_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_limit_req_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_limit_req_module_ctx, /* module context */
|
||||
ngx_http_limit_req_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_int_t
|
||||
ngx_http_limit_req_handler(ngx_http_request_t *r)
|
||||
{
|
||||
uint32_t hash;
|
||||
ngx_str_t key;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t n, excess;
|
||||
ngx_msec_t delay;
|
||||
ngx_http_limit_req_ctx_t *ctx;
|
||||
ngx_http_limit_req_conf_t *lrcf;
|
||||
ngx_http_limit_req_limit_t *limit, *limits;
|
||||
|
||||
if (r->main->limit_req_set) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
|
||||
limits = lrcf->limits.elts;
|
||||
|
||||
excess = 0;
|
||||
|
||||
rc = NGX_DECLINED;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
limit = NULL;
|
||||
#endif
|
||||
|
||||
for (n = 0; n < lrcf->limits.nelts; n++) {
|
||||
|
||||
limit = &limits[n];
|
||||
|
||||
ctx = limit->shm_zone->data;
|
||||
|
||||
if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (key.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.len > 65535) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"the value of the \"%V\" key "
|
||||
"is more than 65535 bytes: \"%V\"",
|
||||
&ctx->key.value, &key);
|
||||
continue;
|
||||
}
|
||||
|
||||
hash = ngx_crc32_short(key.data, key.len);
|
||||
|
||||
ngx_shmtx_lock(&ctx->shpool->mutex);
|
||||
|
||||
rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,
|
||||
(n == lrcf->limits.nelts - 1));
|
||||
|
||||
ngx_shmtx_unlock(&ctx->shpool->mutex);
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"limit_req[%ui]: %i %ui.%03ui",
|
||||
n, rc, excess / 1000, excess % 1000);
|
||||
|
||||
if (rc != NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
r->main->limit_req_set = 1;
|
||||
|
||||
if (rc == NGX_BUSY || rc == NGX_ERROR) {
|
||||
|
||||
if (rc == NGX_BUSY) {
|
||||
ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
|
||||
"limiting requests, excess: %ui.%03ui by zone \"%V\"",
|
||||
excess / 1000, excess % 1000,
|
||||
&limit->shm_zone->shm.name);
|
||||
}
|
||||
|
||||
while (n--) {
|
||||
ctx = limits[n].shm_zone->data;
|
||||
|
||||
if (ctx->node == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_shmtx_lock(&ctx->shpool->mutex);
|
||||
|
||||
ctx->node->count--;
|
||||
|
||||
ngx_shmtx_unlock(&ctx->shpool->mutex);
|
||||
|
||||
ctx->node = NULL;
|
||||
}
|
||||
|
||||
return lrcf->status_code;
|
||||
}
|
||||
|
||||
/* rc == NGX_AGAIN || rc == NGX_OK */
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
excess = 0;
|
||||
}
|
||||
|
||||
delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
|
||||
|
||||
if (!delay) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
|
||||
"delaying request, excess: %ui.%03ui, by zone \"%V\"",
|
||||
excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
|
||||
|
||||
if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
r->read_event_handler = ngx_http_test_reading;
|
||||
r->write_event_handler = ngx_http_limit_req_delay;
|
||||
ngx_add_timer(r->connection->write, delay);
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_limit_req_delay(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_event_t *wev;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"limit_req delay");
|
||||
|
||||
wev = r->connection->write;
|
||||
|
||||
if (!wev->timedout) {
|
||||
|
||||
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
wev->timedout = 0;
|
||||
|
||||
if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
r->read_event_handler = ngx_http_block_reading;
|
||||
r->write_event_handler = ngx_http_core_run_phases;
|
||||
|
||||
ngx_http_core_run_phases(r);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
|
||||
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
||||
{
|
||||
ngx_rbtree_node_t **p;
|
||||
ngx_http_limit_req_node_t *lrn, *lrnt;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (node->key < temp->key) {
|
||||
|
||||
p = &temp->left;
|
||||
|
||||
} else if (node->key > temp->key) {
|
||||
|
||||
p = &temp->right;
|
||||
|
||||
} else { /* node->key == temp->key */
|
||||
|
||||
lrn = (ngx_http_limit_req_node_t *) &node->color;
|
||||
lrnt = (ngx_http_limit_req_node_t *) &temp->color;
|
||||
|
||||
p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
|
||||
? &temp->left : &temp->right;
|
||||
}
|
||||
|
||||
if (*p == sentinel) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp = *p;
|
||||
}
|
||||
|
||||
*p = node;
|
||||
node->parent = temp;
|
||||
node->left = sentinel;
|
||||
node->right = sentinel;
|
||||
ngx_rbt_red(node);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
|
||||
ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)
|
||||
{
|
||||
size_t size;
|
||||
ngx_int_t rc, excess;
|
||||
ngx_time_t *tp;
|
||||
ngx_msec_t now;
|
||||
ngx_msec_int_t ms;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_http_limit_req_ctx_t *ctx;
|
||||
ngx_http_limit_req_node_t *lr;
|
||||
|
||||
tp = ngx_timeofday();
|
||||
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
|
||||
|
||||
ctx = limit->shm_zone->data;
|
||||
|
||||
node = ctx->sh->rbtree.root;
|
||||
sentinel = ctx->sh->rbtree.sentinel;
|
||||
|
||||
while (node != sentinel) {
|
||||
|
||||
if (hash < node->key) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash > node->key) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* hash == node->key */
|
||||
|
||||
lr = (ngx_http_limit_req_node_t *) &node->color;
|
||||
|
||||
rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
|
||||
|
||||
if (rc == 0) {
|
||||
ngx_queue_remove(&lr->queue);
|
||||
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
|
||||
|
||||
ms = (ngx_msec_int_t) (now - lr->last);
|
||||
|
||||
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
|
||||
|
||||
if (excess < 0) {
|
||||
excess = 0;
|
||||
}
|
||||
|
||||
*ep = excess;
|
||||
|
||||
if ((ngx_uint_t) excess > limit->burst) {
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
if (account) {
|
||||
lr->excess = excess;
|
||||
lr->last = now;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
lr->count++;
|
||||
|
||||
ctx->node = lr;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
node = (rc < 0) ? node->left : node->right;
|
||||
}
|
||||
|
||||
*ep = 0;
|
||||
|
||||
size = offsetof(ngx_rbtree_node_t, color)
|
||||
+ offsetof(ngx_http_limit_req_node_t, data)
|
||||
+ key->len;
|
||||
|
||||
ngx_http_limit_req_expire(ctx, 1);
|
||||
|
||||
node = ngx_slab_alloc_locked(ctx->shpool, size);
|
||||
|
||||
if (node == NULL) {
|
||||
ngx_http_limit_req_expire(ctx, 0);
|
||||
|
||||
node = ngx_slab_alloc_locked(ctx->shpool, size);
|
||||
if (node == NULL) {
|
||||
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
||||
"could not allocate node%s", ctx->shpool->log_ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
node->key = hash;
|
||||
|
||||
lr = (ngx_http_limit_req_node_t *) &node->color;
|
||||
|
||||
lr->len = (u_short) key->len;
|
||||
lr->excess = 0;
|
||||
|
||||
ngx_memcpy(lr->data, key->data, key->len);
|
||||
|
||||
ngx_rbtree_insert(&ctx->sh->rbtree, node);
|
||||
|
||||
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
|
||||
|
||||
if (account) {
|
||||
lr->last = now;
|
||||
lr->count = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
lr->last = 0;
|
||||
lr->count = 1;
|
||||
|
||||
ctx->node = lr;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
|
||||
ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
|
||||
{
|
||||
ngx_int_t excess;
|
||||
ngx_time_t *tp;
|
||||
ngx_msec_t now, delay, max_delay;
|
||||
ngx_msec_int_t ms;
|
||||
ngx_http_limit_req_ctx_t *ctx;
|
||||
ngx_http_limit_req_node_t *lr;
|
||||
|
||||
excess = *ep;
|
||||
|
||||
if (excess == 0 || (*limit)->nodelay) {
|
||||
max_delay = 0;
|
||||
|
||||
} else {
|
||||
ctx = (*limit)->shm_zone->data;
|
||||
max_delay = excess * 1000 / ctx->rate;
|
||||
}
|
||||
|
||||
while (n--) {
|
||||
ctx = limits[n].shm_zone->data;
|
||||
lr = ctx->node;
|
||||
|
||||
if (lr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_shmtx_lock(&ctx->shpool->mutex);
|
||||
|
||||
tp = ngx_timeofday();
|
||||
|
||||
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
|
||||
ms = (ngx_msec_int_t) (now - lr->last);
|
||||
|
||||
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
|
||||
|
||||
if (excess < 0) {
|
||||
excess = 0;
|
||||
}
|
||||
|
||||
lr->last = now;
|
||||
lr->excess = excess;
|
||||
lr->count--;
|
||||
|
||||
ngx_shmtx_unlock(&ctx->shpool->mutex);
|
||||
|
||||
ctx->node = NULL;
|
||||
|
||||
if (limits[n].nodelay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
delay = excess * 1000 / ctx->rate;
|
||||
|
||||
if (delay > max_delay) {
|
||||
max_delay = delay;
|
||||
*ep = excess;
|
||||
*limit = &limits[n];
|
||||
}
|
||||
}
|
||||
|
||||
return max_delay;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
|
||||
{
|
||||
ngx_int_t excess;
|
||||
ngx_time_t *tp;
|
||||
ngx_msec_t now;
|
||||
ngx_queue_t *q;
|
||||
ngx_msec_int_t ms;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_http_limit_req_node_t *lr;
|
||||
|
||||
tp = ngx_timeofday();
|
||||
|
||||
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
|
||||
|
||||
/*
|
||||
* n == 1 deletes one or two zero rate entries
|
||||
* n == 0 deletes oldest entry by force
|
||||
* and one or two zero rate entries
|
||||
*/
|
||||
|
||||
while (n < 3) {
|
||||
|
||||
if (ngx_queue_empty(&ctx->sh->queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
q = ngx_queue_last(&ctx->sh->queue);
|
||||
|
||||
lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
|
||||
|
||||
if (lr->count) {
|
||||
|
||||
/*
|
||||
* There is not much sense in looking further,
|
||||
* because we bump nodes on the lookup stage.
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (n++ != 0) {
|
||||
|
||||
ms = (ngx_msec_int_t) (now - lr->last);
|
||||
ms = ngx_abs(ms);
|
||||
|
||||
if (ms < 60000) {
|
||||
return;
|
||||
}
|
||||
|
||||
excess = lr->excess - ctx->rate * ms / 1000;
|
||||
|
||||
if (excess > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_queue_remove(q);
|
||||
|
||||
node = (ngx_rbtree_node_t *)
|
||||
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
|
||||
|
||||
ngx_rbtree_delete(&ctx->sh->rbtree, node);
|
||||
|
||||
ngx_slab_free_locked(ctx->shpool, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
ngx_http_limit_req_ctx_t *octx = data;
|
||||
|
||||
size_t len;
|
||||
ngx_http_limit_req_ctx_t *ctx;
|
||||
|
||||
ctx = shm_zone->data;
|
||||
|
||||
if (octx) {
|
||||
if (ctx->key.value.len != octx->key.value.len
|
||||
|| ngx_strncmp(ctx->key.value.data, octx->key.value.data,
|
||||
ctx->key.value.len)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
|
||||
"limit_req \"%V\" uses the \"%V\" key "
|
||||
"while previously it used the \"%V\" key",
|
||||
&shm_zone->shm.name, &ctx->key.value,
|
||||
&octx->key.value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->sh = octx->sh;
|
||||
ctx->shpool = octx->shpool;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
|
||||
if (shm_zone->shm.exists) {
|
||||
ctx->sh = ctx->shpool->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
|
||||
if (ctx->sh == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->shpool->data = ctx->sh;
|
||||
|
||||
ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
|
||||
ngx_http_limit_req_rbtree_insert_value);
|
||||
|
||||
ngx_queue_init(&ctx->sh->queue);
|
||||
|
||||
len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
|
||||
|
||||
ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
|
||||
if (ctx->shpool->log_ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
|
||||
&shm_zone->shm.name);
|
||||
|
||||
ctx->shpool->log_nomem = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_limit_req_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_limit_req_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->limits.elts = NULL;
|
||||
*/
|
||||
|
||||
conf->limit_log_level = NGX_CONF_UNSET_UINT;
|
||||
conf->status_code = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_limit_req_conf_t *prev = parent;
|
||||
ngx_http_limit_req_conf_t *conf = child;
|
||||
|
||||
if (conf->limits.elts == NULL) {
|
||||
conf->limits = prev->limits;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
|
||||
NGX_LOG_ERR);
|
||||
|
||||
conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
|
||||
NGX_LOG_INFO : conf->limit_log_level + 1;
|
||||
|
||||
ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
|
||||
NGX_HTTP_SERVICE_UNAVAILABLE);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ssize_t size;
|
||||
ngx_str_t *value, name, s;
|
||||
ngx_int_t rate, scale;
|
||||
ngx_uint_t i;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_http_limit_req_ctx_t *ctx;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &ctx->key;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
rate = 1;
|
||||
scale = 1;
|
||||
name.len = 0;
|
||||
|
||||
for (i = 2; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
|
||||
|
||||
name.data = value[i].data + 5;
|
||||
|
||||
p = (u_char *) ngx_strchr(name.data, ':');
|
||||
|
||||
if (p == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len = p - name.data;
|
||||
|
||||
s.data = p + 1;
|
||||
s.len = value[i].data + value[i].len - s.data;
|
||||
|
||||
size = ngx_parse_size(&s);
|
||||
|
||||
if (size == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (size < (ssize_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"zone \"%V\" is too small", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
|
||||
|
||||
len = value[i].len;
|
||||
p = value[i].data + len - 3;
|
||||
|
||||
if (ngx_strncmp(p, "r/s", 3) == 0) {
|
||||
scale = 1;
|
||||
len -= 3;
|
||||
|
||||
} else if (ngx_strncmp(p, "r/m", 3) == 0) {
|
||||
scale = 60;
|
||||
len -= 3;
|
||||
}
|
||||
|
||||
rate = ngx_atoi(value[i].data + 5, len - 5);
|
||||
if (rate <= 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid rate \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (name.len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"%V\" must have \"zone\" parameter",
|
||||
&cmd->name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx->rate = rate * 1000 / scale;
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &name, size,
|
||||
&ngx_http_limit_req_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (shm_zone->data) {
|
||||
ctx = shm_zone->data;
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%V \"%V\" is already bound to key \"%V\"",
|
||||
&cmd->name, &name, &ctx->key.value);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
shm_zone->init = ngx_http_limit_req_init_zone;
|
||||
shm_zone->data = ctx;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_limit_req_conf_t *lrcf = conf;
|
||||
|
||||
ngx_int_t burst;
|
||||
ngx_str_t *value, s;
|
||||
ngx_uint_t i, nodelay;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_http_limit_req_limit_t *limit, *limits;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
shm_zone = NULL;
|
||||
burst = 0;
|
||||
nodelay = 0;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
|
||||
|
||||
s.len = value[i].len - 5;
|
||||
s.data = value[i].data + 5;
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &s, 0,
|
||||
&ngx_http_limit_req_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
|
||||
|
||||
burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
|
||||
if (burst <= 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid burst rate \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "nodelay") == 0) {
|
||||
nodelay = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (shm_zone == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"%V\" must have \"zone\" parameter",
|
||||
&cmd->name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (shm_zone->data == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"unknown limit_req_zone \"%V\"",
|
||||
&shm_zone->shm.name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limits = lrcf->limits.elts;
|
||||
|
||||
if (limits == NULL) {
|
||||
if (ngx_array_init(&lrcf->limits, cf->pool, 1,
|
||||
sizeof(ngx_http_limit_req_limit_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lrcf->limits.nelts; i++) {
|
||||
if (shm_zone == limits[i].shm_zone) {
|
||||
return "is duplicate";
|
||||
}
|
||||
}
|
||||
|
||||
limit = ngx_array_push(&lrcf->limits);
|
||||
if (limit == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limit->shm_zone = shm_zone;
|
||||
limit->burst = burst * 1000;
|
||||
limit->nodelay = nodelay;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_limit_req_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_limit_req_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,567 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t hash_max_size;
|
||||
ngx_uint_t hash_bucket_size;
|
||||
} ngx_http_map_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_keys_arrays_t keys;
|
||||
|
||||
ngx_array_t *values_hash;
|
||||
ngx_array_t var_values;
|
||||
#if (NGX_PCRE)
|
||||
ngx_array_t regexes;
|
||||
#endif
|
||||
|
||||
ngx_http_variable_value_t *default_value;
|
||||
ngx_conf_t *cf;
|
||||
ngx_uint_t hostnames; /* unsigned hostnames:1 */
|
||||
} ngx_http_map_conf_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_map_t map;
|
||||
ngx_http_complex_value_t value;
|
||||
ngx_http_variable_value_t *default_value;
|
||||
ngx_uint_t hostnames; /* unsigned hostnames:1 */
|
||||
} ngx_http_map_ctx_t;
|
||||
|
||||
|
||||
static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
|
||||
const void *two);
|
||||
static void *ngx_http_map_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_map_commands[] = {
|
||||
|
||||
{ ngx_string("map"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
|
||||
ngx_http_map_block,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("map_hash_max_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_map_conf_t, hash_max_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("map_hash_bucket_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_map_conf_t, hash_bucket_size),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_map_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_http_map_create_conf, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_map_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_map_module_ctx, /* module context */
|
||||
ngx_http_map_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_int_t
|
||||
ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
|
||||
|
||||
ngx_str_t val;
|
||||
ngx_http_variable_value_t *value;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http map started");
|
||||
|
||||
if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
|
||||
val.len--;
|
||||
}
|
||||
|
||||
value = ngx_http_map_find(r, &map->map, &val);
|
||||
|
||||
if (value == NULL) {
|
||||
value = map->default_value;
|
||||
}
|
||||
|
||||
if (!value->valid) {
|
||||
value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data);
|
||||
|
||||
if (value == NULL || value->not_found) {
|
||||
value = &ngx_http_variable_null_value;
|
||||
}
|
||||
}
|
||||
|
||||
*v = *value;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http map: \"%v\" \"%v\"", &val, v);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_map_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_map_conf_t *mcf;
|
||||
|
||||
mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
|
||||
if (mcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mcf->hash_max_size = NGX_CONF_UNSET_UINT;
|
||||
mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return mcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_map_conf_t *mcf = conf;
|
||||
|
||||
char *rv;
|
||||
ngx_str_t *value, name;
|
||||
ngx_conf_t save;
|
||||
ngx_pool_t *pool;
|
||||
ngx_hash_init_t hash;
|
||||
ngx_http_map_ctx_t *map;
|
||||
ngx_http_variable_t *var;
|
||||
ngx_http_map_conf_ctx_t ctx;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
|
||||
mcf->hash_max_size = 2048;
|
||||
}
|
||||
|
||||
if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
|
||||
mcf->hash_bucket_size = ngx_cacheline_size;
|
||||
|
||||
} else {
|
||||
mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
|
||||
ngx_cacheline_size);
|
||||
}
|
||||
|
||||
map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
|
||||
if (map == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &map->value;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name = value[2];
|
||||
|
||||
if (name.data[0] != '$') {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid variable name \"%V\"", &name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len--;
|
||||
name.data++;
|
||||
|
||||
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_map_variable;
|
||||
var->data = (uintptr_t) map;
|
||||
|
||||
pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
|
||||
if (pool == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx.keys.pool = cf->pool;
|
||||
ctx.keys.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
|
||||
if (ctx.values_hash == NULL) {
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&ctx.var_values, cf->pool, 2,
|
||||
sizeof(ngx_http_variable_value_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx.default_value = NULL;
|
||||
ctx.cf = &save;
|
||||
ctx.hostnames = 0;
|
||||
|
||||
save = *cf;
|
||||
cf->pool = pool;
|
||||
cf->ctx = &ctx;
|
||||
cf->handler = ngx_http_map;
|
||||
cf->handler_conf = conf;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = save;
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
ngx_destroy_pool(pool);
|
||||
return rv;
|
||||
}
|
||||
|
||||
map->default_value = ctx.default_value ? ctx.default_value:
|
||||
&ngx_http_variable_null_value;
|
||||
|
||||
map->hostnames = ctx.hostnames;
|
||||
|
||||
hash.key = ngx_hash_key_lc;
|
||||
hash.max_size = mcf->hash_max_size;
|
||||
hash.bucket_size = mcf->hash_bucket_size;
|
||||
hash.name = "map_hash";
|
||||
hash.pool = cf->pool;
|
||||
|
||||
if (ctx.keys.keys.nelts) {
|
||||
hash.hash = &map->map.hash.hash;
|
||||
hash.temp_pool = NULL;
|
||||
|
||||
if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.keys.dns_wc_head.nelts) {
|
||||
|
||||
ngx_qsort(ctx.keys.dns_wc_head.elts,
|
||||
(size_t) ctx.keys.dns_wc_head.nelts,
|
||||
sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
|
||||
ctx.keys.dns_wc_head.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
if (ctx.keys.dns_wc_tail.nelts) {
|
||||
|
||||
ngx_qsort(ctx.keys.dns_wc_tail.elts,
|
||||
(size_t) ctx.keys.dns_wc_tail.nelts,
|
||||
sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
|
||||
ctx.keys.dns_wc_tail.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
if (ctx.regexes.nelts) {
|
||||
map->map.regex = ctx.regexes.elts;
|
||||
map->map.nregex = ctx.regexes.nelts;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ngx_destroy_pool(pool);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
|
||||
{
|
||||
ngx_hash_key_t *first, *second;
|
||||
|
||||
first = (ngx_hash_key_t *) one;
|
||||
second = (ngx_hash_key_t *) two;
|
||||
|
||||
return ngx_dns_strcmp(first->key.data, second->key.data);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
{
|
||||
ngx_int_t rc, index;
|
||||
ngx_str_t *value, 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) {
|
||||
return ngx_conf_include(cf, dummy, conf);
|
||||
}
|
||||
|
||||
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 == (intptr_t) var[i].data) {
|
||||
var = &var[i];
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
var = ngx_array_push(&ctx->var_values);
|
||||
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 *) (intptr_t) index;
|
||||
|
||||
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));
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_conf_t upstream;
|
||||
ngx_int_t index;
|
||||
ngx_uint_t gzip_flag;
|
||||
} ngx_http_memcached_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t rest;
|
||||
ngx_http_request_t *request;
|
||||
ngx_str_t key;
|
||||
} ngx_http_memcached_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_memcached_filter_init(void *data);
|
||||
static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
|
||||
static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
|
||||
static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
|
||||
ngx_int_t rc);
|
||||
|
||||
static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
|
||||
static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_conf_bitmask_t ngx_http_memcached_next_upstream_masks[] = {
|
||||
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
|
||||
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
|
||||
{ ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
|
||||
{ ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
|
||||
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_memcached_commands[] = {
|
||||
|
||||
{ ngx_string("memcached_pass"),
|
||||
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_memcached_pass,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_bind"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_upstream_bind_set_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_connect_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_send_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_buffer_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_read_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_next_upstream"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_conf_set_bitmask_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
|
||||
&ngx_http_memcached_next_upstream_masks },
|
||||
|
||||
{ ngx_string("memcached_next_upstream_tries"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_tries),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_next_upstream_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("memcached_gzip_flag"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_memcached_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_memcached_create_loc_conf, /* create location configuration */
|
||||
ngx_http_memcached_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_memcached_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_memcached_module_ctx, /* module context */
|
||||
ngx_http_memcached_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_str_t ngx_http_memcached_key = ngx_string("memcached_key");
|
||||
|
||||
|
||||
#define NGX_HTTP_MEMCACHED_END (sizeof(ngx_http_memcached_end) - 1)
|
||||
static u_char ngx_http_memcached_end[] = CRLF "END" CRLF;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_memcached_ctx_t *ctx;
|
||||
ngx_http_memcached_loc_conf_t *mlcf;
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
rc = ngx_http_discard_request_body(r);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ngx_http_set_content_type(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_upstream_create(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
ngx_str_set(&u->schema, "memcached://");
|
||||
u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
|
||||
|
||||
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
|
||||
|
||||
u->conf = &mlcf->upstream;
|
||||
|
||||
u->create_request = ngx_http_memcached_create_request;
|
||||
u->reinit_request = ngx_http_memcached_reinit_request;
|
||||
u->process_header = ngx_http_memcached_process_header;
|
||||
u->abort_request = ngx_http_memcached_abort_request;
|
||||
u->finalize_request = ngx_http_memcached_finalize_request;
|
||||
|
||||
ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx->request = r;
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
|
||||
|
||||
u->input_filter_init = ngx_http_memcached_filter_init;
|
||||
u->input_filter = ngx_http_memcached_filter;
|
||||
u->input_filter_ctx = ctx;
|
||||
|
||||
r->main->count++;
|
||||
|
||||
ngx_http_upstream_init(r);
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_create_request(ngx_http_request_t *r)
|
||||
{
|
||||
size_t len;
|
||||
uintptr_t escape;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_memcached_ctx_t *ctx;
|
||||
ngx_http_variable_value_t *vv;
|
||||
ngx_http_memcached_loc_conf_t *mlcf;
|
||||
|
||||
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
|
||||
|
||||
vv = ngx_http_get_indexed_variable(r, mlcf->index);
|
||||
|
||||
if (vv == NULL || vv->not_found || vv->len == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"the \"$memcached_key\" variable is not set");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);
|
||||
|
||||
len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, len);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = b;
|
||||
cl->next = NULL;
|
||||
|
||||
r->upstream->request_bufs = cl;
|
||||
|
||||
*b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
|
||||
|
||||
ctx->key.data = b->last;
|
||||
|
||||
if (escape == 0) {
|
||||
b->last = ngx_copy(b->last, vv->data, vv->len);
|
||||
|
||||
} else {
|
||||
b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
|
||||
NGX_ESCAPE_MEMCACHED);
|
||||
}
|
||||
|
||||
ctx->key.len = b->last - ctx->key.data;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http memcached request: \"%V\"", &ctx->key);
|
||||
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_reinit_request(ngx_http_request_t *r)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_process_header(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p, *start;
|
||||
ngx_str_t line;
|
||||
ngx_uint_t flags;
|
||||
ngx_table_elt_t *h;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_memcached_ctx_t *ctx;
|
||||
ngx_http_memcached_loc_conf_t *mlcf;
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
for (p = u->buffer.pos; p < u->buffer.last; p++) {
|
||||
if (*p == LF) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
|
||||
found:
|
||||
|
||||
line.data = u->buffer.pos;
|
||||
line.len = p - u->buffer.pos;
|
||||
|
||||
if (line.len == 0 || *(p - 1) != CR) {
|
||||
goto no_valid;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
line.len--;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"memcached: \"%V\"", &line);
|
||||
|
||||
p = u->buffer.pos;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
|
||||
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
|
||||
|
||||
if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
|
||||
|
||||
p += sizeof("VALUE ") - 1;
|
||||
|
||||
if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"memcached sent invalid key in response \"%V\" "
|
||||
"for key \"%V\"",
|
||||
&line, &ctx->key);
|
||||
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
p += ctx->key.len;
|
||||
|
||||
if (*p++ != ' ') {
|
||||
goto no_valid;
|
||||
}
|
||||
|
||||
/* flags */
|
||||
|
||||
start = p;
|
||||
|
||||
while (*p) {
|
||||
if (*p++ == ' ') {
|
||||
if (mlcf->gzip_flag) {
|
||||
goto flags;
|
||||
} else {
|
||||
goto length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto no_valid;
|
||||
|
||||
flags:
|
||||
|
||||
flags = ngx_atoi(start, p - start - 1);
|
||||
|
||||
if (flags == (ngx_uint_t) NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"memcached sent invalid flags in response \"%V\" "
|
||||
"for key \"%V\"",
|
||||
&line, &ctx->key);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (flags & mlcf->gzip_flag) {
|
||||
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;
|
||||
}
|
||||
|
||||
length:
|
||||
|
||||
start = p;
|
||||
p = line.data + line.len;
|
||||
|
||||
u->headers_in.content_length_n = ngx_atoof(start, p - start);
|
||||
if (u->headers_in.content_length_n == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"memcached sent invalid length in response \"%V\" "
|
||||
"for key \"%V\"",
|
||||
&line, &ctx->key);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
u->headers_in.status_n = 200;
|
||||
u->state->status = 200;
|
||||
u->buffer.pos = p + sizeof(CRLF) - 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(p, "END\x0d") == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"key: \"%V\" was not found by memcached", &ctx->key);
|
||||
|
||||
u->headers_in.content_length_n = 0;
|
||||
u->headers_in.status_n = 404;
|
||||
u->state->status = 404;
|
||||
u->buffer.pos = p + sizeof("END" CRLF) - 1;
|
||||
u->keepalive = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
no_valid:
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"memcached sent invalid response: \"%V\"", &line);
|
||||
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_filter_init(void *data)
|
||||
{
|
||||
ngx_http_memcached_ctx_t *ctx = data;
|
||||
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
u = ctx->request->upstream;
|
||||
|
||||
if (u->headers_in.status_n != 404) {
|
||||
u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
|
||||
ctx->rest = NGX_HTTP_MEMCACHED_END;
|
||||
|
||||
} else {
|
||||
u->length = 0;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_memcached_filter(void *data, ssize_t bytes)
|
||||
{
|
||||
ngx_http_memcached_ctx_t *ctx = data;
|
||||
|
||||
u_char *last;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
u = ctx->request->upstream;
|
||||
b = &u->buffer;
|
||||
|
||||
if (u->length == (ssize_t) ctx->rest) {
|
||||
|
||||
if (ngx_strncmp(b->last,
|
||||
ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
|
||||
bytes)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
|
||||
"memcached sent invalid trailer");
|
||||
|
||||
u->length = 0;
|
||||
ctx->rest = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
u->length -= bytes;
|
||||
ctx->rest -= bytes;
|
||||
|
||||
if (u->length == 0) {
|
||||
u->keepalive = 1;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf->flush = 1;
|
||||
cl->buf->memory = 1;
|
||||
|
||||
*ll = cl;
|
||||
|
||||
last = b->last;
|
||||
cl->buf->pos = last;
|
||||
b->last += bytes;
|
||||
cl->buf->last = b->last;
|
||||
cl->buf->tag = u->output.tag;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
|
||||
"memcached filter bytes:%z size:%z length:%z rest:%z",
|
||||
bytes, b->last - b->pos, u->length, ctx->rest);
|
||||
|
||||
if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
|
||||
u->length -= bytes;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
|
||||
|
||||
if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
|
||||
"memcached sent invalid trailer");
|
||||
|
||||
b->last = last;
|
||||
cl->buf->last = last;
|
||||
u->length = 0;
|
||||
ctx->rest = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->rest -= b->last - last;
|
||||
b->last = last;
|
||||
cl->buf->last = last;
|
||||
u->length = ctx->rest;
|
||||
|
||||
if (u->length == 0) {
|
||||
u->keepalive = 1;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_memcached_abort_request(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"abort http memcached request");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"finalize http memcached request");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_memcached_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->upstream.bufs.num = 0;
|
||||
* conf->upstream.next_upstream = 0;
|
||||
* conf->upstream.temp_path = NULL;
|
||||
* conf->upstream.uri = { 0, NULL };
|
||||
* conf->upstream.location = NULL;
|
||||
*/
|
||||
|
||||
conf->upstream.local = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
|
||||
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
|
||||
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
|
||||
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
|
||||
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
|
||||
|
||||
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
/* the hardcoded values */
|
||||
conf->upstream.cyclic_temp_file = 0;
|
||||
conf->upstream.buffering = 0;
|
||||
conf->upstream.ignore_client_abort = 0;
|
||||
conf->upstream.send_lowat = 0;
|
||||
conf->upstream.bufs.num = 0;
|
||||
conf->upstream.busy_buffers_size = 0;
|
||||
conf->upstream.max_temp_file_size = 0;
|
||||
conf->upstream.temp_file_write_size = 0;
|
||||
conf->upstream.intercept_errors = 1;
|
||||
conf->upstream.intercept_404 = 1;
|
||||
conf->upstream.pass_request_headers = 0;
|
||||
conf->upstream.pass_request_body = 0;
|
||||
|
||||
conf->index = NGX_CONF_UNSET;
|
||||
conf->gzip_flag = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_memcached_loc_conf_t *prev = parent;
|
||||
ngx_http_memcached_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->upstream.local,
|
||||
prev->upstream.local, NULL);
|
||||
|
||||
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
|
||||
prev->upstream.next_upstream_tries, 0);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
|
||||
prev->upstream.connect_timeout, 60000);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
|
||||
prev->upstream.send_timeout, 60000);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
|
||||
prev->upstream.read_timeout, 60000);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
|
||||
prev->upstream.next_upstream_timeout, 0);
|
||||
|
||||
ngx_conf_merge_size_value(conf->upstream.buffer_size,
|
||||
prev->upstream.buffer_size,
|
||||
(size_t) ngx_pagesize);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
|
||||
prev->upstream.next_upstream,
|
||||
(NGX_CONF_BITMASK_SET
|
||||
|NGX_HTTP_UPSTREAM_FT_ERROR
|
||||
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
|
||||
|
||||
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
|
||||
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|
||||
|NGX_HTTP_UPSTREAM_FT_OFF;
|
||||
}
|
||||
|
||||
if (conf->upstream.upstream == NULL) {
|
||||
conf->upstream.upstream = prev->upstream.upstream;
|
||||
}
|
||||
|
||||
if (conf->index == NGX_CONF_UNSET) {
|
||||
conf->index = prev->index;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_memcached_loc_conf_t *mlcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_url_t u;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (mlcf->upstream.upstream) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
u.url = value[1];
|
||||
u.no_resolve = 1;
|
||||
|
||||
mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
|
||||
if (mlcf->upstream.upstream == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
|
||||
clcf->handler = ngx_http_memcached_handler;
|
||||
|
||||
if (clcf->name.data[clcf->name.len - 1] == '/') {
|
||||
clcf->auto_redirect = 1;
|
||||
}
|
||||
|
||||
mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
|
||||
|
||||
if (mlcf->index == NGX_ERROR) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
|
||||
static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
|
||||
static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *header, ngx_uint_t weak);
|
||||
static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_not_modified_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_not_modified_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_not_modified_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_not_modified_filter_module_ctx, /* module context */
|
||||
NULL, /* 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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
|
||||
{
|
||||
if (r->headers_out.status != NGX_HTTP_OK
|
||||
|| r != r->main
|
||||
|| r->disable_not_modified)
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
if (r->headers_in.if_unmodified_since
|
||||
&& !ngx_http_test_if_unmodified(r))
|
||||
{
|
||||
return ngx_http_filter_finalize_request(r, NULL,
|
||||
NGX_HTTP_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
if (r->headers_in.if_match
|
||||
&& !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
|
||||
{
|
||||
return ngx_http_filter_finalize_request(r, NULL,
|
||||
NGX_HTTP_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
|
||||
|
||||
if (r->headers_in.if_modified_since
|
||||
&& ngx_http_test_if_modified(r))
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
if (r->headers_in.if_none_match
|
||||
&& !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
/* not modified */
|
||||
|
||||
r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
|
||||
r->headers_out.status_line.len = 0;
|
||||
r->headers_out.content_type.len = 0;
|
||||
ngx_http_clear_content_length(r);
|
||||
ngx_http_clear_accept_ranges(r);
|
||||
|
||||
if (r->headers_out.content_encoding) {
|
||||
r->headers_out.content_encoding->hash = 0;
|
||||
r->headers_out.content_encoding = NULL;
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_http_test_if_unmodified(ngx_http_request_t *r)
|
||||
{
|
||||
time_t iums;
|
||||
|
||||
if (r->headers_out.last_modified_time == (time_t) -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
|
||||
r->headers_in.if_unmodified_since->value.len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http iums:%T lm:%T", iums, r->headers_out.last_modified_time);
|
||||
|
||||
if (iums >= r->headers_out.last_modified_time) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_http_test_if_modified(ngx_http_request_t *r)
|
||||
{
|
||||
time_t ims;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (r->headers_out.last_modified_time == (time_t) -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
|
||||
r->headers_in.if_modified_since->value.len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http ims:%T lm:%T", ims, r->headers_out.last_modified_time);
|
||||
|
||||
if (ims == r->headers_out.last_modified_time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
|
||||
|| ims < r->headers_out.last_modified_time)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,
|
||||
ngx_uint_t weak)
|
||||
{
|
||||
u_char *start, *end, ch;
|
||||
ngx_str_t etag, *list;
|
||||
|
||||
list = &header->value;
|
||||
|
||||
if (list->len == 1 && list->data[0] == '*') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (r->headers_out.etag == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
etag = r->headers_out.etag->value;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http im:\"%V\" etag:%V", list, &etag);
|
||||
|
||||
if (weak
|
||||
&& etag.len > 2
|
||||
&& etag.data[0] == 'W'
|
||||
&& etag.data[1] == '/')
|
||||
{
|
||||
etag.len -= 2;
|
||||
etag.data += 2;
|
||||
}
|
||||
|
||||
start = list->data;
|
||||
end = list->data + list->len;
|
||||
|
||||
while (start < end) {
|
||||
|
||||
if (weak
|
||||
&& end - start > 2
|
||||
&& start[0] == 'W'
|
||||
&& start[1] == '/')
|
||||
{
|
||||
start += 2;
|
||||
}
|
||||
|
||||
if (etag.len > (size_t) (end - start)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(start, etag.data, etag.len) != 0) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
start += etag.len;
|
||||
|
||||
while (start < end) {
|
||||
ch = *start;
|
||||
|
||||
if (ch == ' ' || ch == '\t') {
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (start == end || *start == ',') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
skip:
|
||||
|
||||
while (start < end && *start != ',') { start++; }
|
||||
while (start < end) {
|
||||
ch = *start;
|
||||
|
||||
if (ch == ' ' || ch == '\t' || ch == ',') {
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_not_modified_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,317 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_flag_t enable;
|
||||
} ngx_http_random_index_loc_conf_t;
|
||||
|
||||
|
||||
#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE 50
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,
|
||||
ngx_dir_t *dir, ngx_str_t *name);
|
||||
static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);
|
||||
static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_random_index_commands[] = {
|
||||
|
||||
{ ngx_string("random_index"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_random_index_loc_conf_t, enable),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_random_index_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_random_index_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_random_index_create_loc_conf, /* create location configuration */
|
||||
ngx_http_random_index_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_random_index_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_random_index_module_ctx, /* module context */
|
||||
ngx_http_random_index_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_int_t
|
||||
ngx_http_random_index_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *last, *filename;
|
||||
size_t len, allocated, root;
|
||||
ngx_err_t err;
|
||||
ngx_int_t rc;
|
||||
ngx_str_t path, uri, *name;
|
||||
ngx_dir_t dir;
|
||||
ngx_uint_t n, level;
|
||||
ngx_array_t names;
|
||||
ngx_http_random_index_loc_conf_t *rlcf;
|
||||
|
||||
if (r->uri.data[r->uri.len - 1] != '/') {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);
|
||||
|
||||
if (!rlcf->enable) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_D_TYPE)
|
||||
len = NGX_DIR_MASK_LEN;
|
||||
#else
|
||||
len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
|
||||
#endif
|
||||
|
||||
last = ngx_http_map_uri_to_path(r, &path, &root, len);
|
||||
if (last == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
allocated = path.len;
|
||||
|
||||
path.len = last - path.data - 1;
|
||||
path.data[path.len] = '\0';
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http random index: \"%s\"", path.data);
|
||||
|
||||
if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
|
||||
err = ngx_errno;
|
||||
|
||||
if (err == NGX_ENOENT
|
||||
|| err == NGX_ENOTDIR
|
||||
|| err == NGX_ENAMETOOLONG)
|
||||
{
|
||||
level = NGX_LOG_ERR;
|
||||
rc = NGX_HTTP_NOT_FOUND;
|
||||
|
||||
} else if (err == NGX_EACCES) {
|
||||
level = NGX_LOG_ERR;
|
||||
rc = NGX_HTTP_FORBIDDEN;
|
||||
|
||||
} else {
|
||||
level = NGX_LOG_CRIT;
|
||||
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_error(level, r->connection->log, err,
|
||||
ngx_open_dir_n " \"%s\" failed", path.data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
filename = path.data;
|
||||
filename[path.len] = '/';
|
||||
|
||||
for ( ;; ) {
|
||||
ngx_set_errno(0);
|
||||
|
||||
if (ngx_read_dir(&dir) == NGX_ERROR) {
|
||||
err = ngx_errno;
|
||||
|
||||
if (err != NGX_ENOMOREFILES) {
|
||||
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
|
||||
ngx_read_dir_n " \"%V\" failed", &path);
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http random index file: \"%s\"", ngx_de_name(&dir));
|
||||
|
||||
if (ngx_de_name(&dir)[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = ngx_de_namelen(&dir);
|
||||
|
||||
if (dir.type == 0 || ngx_de_is_link(&dir)) {
|
||||
|
||||
/* 1 byte for '/' and 1 byte for terminating '\0' */
|
||||
|
||||
if (path.len + 1 + len + 1 > allocated) {
|
||||
allocated = path.len + 1 + len + 1
|
||||
+ NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
|
||||
|
||||
filename = ngx_pnalloc(r->pool, allocated);
|
||||
if (filename == NULL) {
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
last = ngx_cpystrn(filename, path.data, path.len + 1);
|
||||
*last++ = '/';
|
||||
}
|
||||
|
||||
ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
|
||||
|
||||
if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
|
||||
err = ngx_errno;
|
||||
|
||||
if (err != NGX_ENOENT) {
|
||||
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
|
||||
ngx_de_info_n " \"%s\" failed", filename);
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
|
||||
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
|
||||
ngx_de_link_info_n " \"%s\" failed",
|
||||
filename);
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ngx_de_is_file(&dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name = ngx_array_push(&names);
|
||||
if (name == NULL) {
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
name->len = len;
|
||||
|
||||
name->data = ngx_pnalloc(r->pool, len);
|
||||
if (name->data == NULL) {
|
||||
return ngx_http_random_index_error(r, &dir, &path);
|
||||
}
|
||||
|
||||
ngx_memcpy(name->data, ngx_de_name(&dir), len);
|
||||
}
|
||||
|
||||
if (ngx_close_dir(&dir) == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
|
||||
ngx_close_dir_n " \"%s\" failed", &path);
|
||||
}
|
||||
|
||||
n = names.nelts;
|
||||
|
||||
if (n == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
name = names.elts;
|
||||
|
||||
n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);
|
||||
|
||||
uri.len = r->uri.len + name[n].len;
|
||||
|
||||
uri.data = ngx_pnalloc(r->pool, uri.len);
|
||||
if (uri.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
last = ngx_copy(uri.data, r->uri.data, r->uri.len);
|
||||
ngx_memcpy(last, name[n].data, name[n].len);
|
||||
|
||||
return ngx_http_internal_redirect(r, &uri, &r->args);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,
|
||||
ngx_str_t *name)
|
||||
{
|
||||
if (ngx_close_dir(dir) == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
|
||||
ngx_close_dir_n " \"%V\" failed", name);
|
||||
}
|
||||
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_random_index_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_random_index_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->enable = NGX_CONF_UNSET;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_random_index_loc_conf_t *prev = parent;
|
||||
ngx_http_random_index_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_value(conf->enable, prev->enable, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_random_index_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_random_index_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,901 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
/*
|
||||
* the single part format:
|
||||
*
|
||||
* "HTTP/1.0 206 Partial Content" CRLF
|
||||
* ... header ...
|
||||
* "Content-Type: image/jpeg" CRLF
|
||||
* "Content-Length: SIZE" CRLF
|
||||
* "Content-Range: bytes START-END/SIZE" CRLF
|
||||
* CRLF
|
||||
* ... data ...
|
||||
*
|
||||
*
|
||||
* the multipart format:
|
||||
*
|
||||
* "HTTP/1.0 206 Partial Content" CRLF
|
||||
* ... header ...
|
||||
* "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
|
||||
* CRLF
|
||||
* CRLF
|
||||
* "--0123456789" CRLF
|
||||
* "Content-Type: image/jpeg" CRLF
|
||||
* "Content-Range: bytes START0-END0/SIZE" CRLF
|
||||
* CRLF
|
||||
* ... data ...
|
||||
* CRLF
|
||||
* "--0123456789" CRLF
|
||||
* "Content-Type: image/jpeg" CRLF
|
||||
* "Content-Range: bytes START1-END1/SIZE" CRLF
|
||||
* CRLF
|
||||
* ... data ...
|
||||
* CRLF
|
||||
* "--0123456789--" CRLF
|
||||
*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t start;
|
||||
off_t end;
|
||||
ngx_str_t content_range;
|
||||
} ngx_http_range_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t offset;
|
||||
ngx_str_t boundary_header;
|
||||
ngx_array_t ranges;
|
||||
} ngx_http_range_filter_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
|
||||
static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
|
||||
static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
|
||||
static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
|
||||
|
||||
static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_range_header_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_range_header_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL, /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_range_header_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_range_header_filter_module_ctx, /* module context */
|
||||
NULL, /* 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_http_module_t ngx_http_range_body_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_range_body_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL, /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_range_body_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_range_body_filter_module_ctx, /* module context */
|
||||
NULL, /* 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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_header_filter(ngx_http_request_t *r)
|
||||
{
|
||||
time_t if_range_time;
|
||||
ngx_str_t *if_range, *etag;
|
||||
ngx_uint_t ranges;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_range_filter_ctx_t *ctx;
|
||||
|
||||
if (r->http_version < NGX_HTTP_VERSION_10
|
||||
|| r->headers_out.status != NGX_HTTP_OK
|
||||
|| r != r->main
|
||||
|| r->headers_out.content_length_n == -1
|
||||
|| !r->allow_ranges)
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->max_ranges == 0) {
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
if (r->headers_in.range == NULL
|
||||
|| r->headers_in.range->value.len < 7
|
||||
|| ngx_strncasecmp(r->headers_in.range->value.data,
|
||||
(u_char *) "bytes=", 6)
|
||||
!= 0)
|
||||
{
|
||||
goto next_filter;
|
||||
}
|
||||
|
||||
if (r->headers_in.if_range) {
|
||||
|
||||
if_range = &r->headers_in.if_range->value;
|
||||
|
||||
if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
|
||||
|
||||
if (r->headers_out.etag == NULL) {
|
||||
goto next_filter;
|
||||
}
|
||||
|
||||
etag = &r->headers_out.etag->value;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http ir:%V etag:%V", if_range, etag);
|
||||
|
||||
if (if_range->len != etag->len
|
||||
|| ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
|
||||
{
|
||||
goto next_filter;
|
||||
}
|
||||
|
||||
goto parse;
|
||||
}
|
||||
|
||||
if (r->headers_out.last_modified_time == (time_t) -1) {
|
||||
goto next_filter;
|
||||
}
|
||||
|
||||
if_range_time = ngx_http_parse_time(if_range->data, if_range->len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http ir:%d lm:%d",
|
||||
if_range_time, r->headers_out.last_modified_time);
|
||||
|
||||
if (if_range_time != r->headers_out.last_modified_time) {
|
||||
goto next_filter;
|
||||
}
|
||||
}
|
||||
|
||||
parse:
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ranges = r->single_range ? 1 : clcf->max_ranges;
|
||||
|
||||
switch (ngx_http_range_parse(r, ctx, ranges)) {
|
||||
|
||||
case NGX_OK:
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
|
||||
|
||||
r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
|
||||
r->headers_out.status_line.len = 0;
|
||||
|
||||
if (ctx->ranges.nelts == 1) {
|
||||
return ngx_http_range_singlepart_header(r, ctx);
|
||||
}
|
||||
|
||||
return ngx_http_range_multipart_header(r, ctx);
|
||||
|
||||
case NGX_HTTP_RANGE_NOT_SATISFIABLE:
|
||||
return ngx_http_range_not_satisfiable(r);
|
||||
|
||||
case NGX_ERROR:
|
||||
return NGX_ERROR;
|
||||
|
||||
default: /* NGX_DECLINED */
|
||||
break;
|
||||
}
|
||||
|
||||
next_filter:
|
||||
|
||||
r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
|
||||
if (r->headers_out.accept_ranges == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.accept_ranges->hash = 1;
|
||||
ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
|
||||
ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
|
||||
ngx_uint_t ranges)
|
||||
{
|
||||
u_char *p;
|
||||
off_t start, end, size, content_length, cutoff, cutlim;
|
||||
ngx_uint_t suffix;
|
||||
ngx_http_range_t *range;
|
||||
|
||||
p = r->headers_in.range->value.data + 6;
|
||||
size = 0;
|
||||
content_length = r->headers_out.content_length_n;
|
||||
|
||||
cutoff = NGX_MAX_OFF_T_VALUE / 10;
|
||||
cutlim = NGX_MAX_OFF_T_VALUE % 10;
|
||||
|
||||
for ( ;; ) {
|
||||
start = 0;
|
||||
end = 0;
|
||||
suffix = 0;
|
||||
|
||||
while (*p == ' ') { p++; }
|
||||
|
||||
if (*p != '-') {
|
||||
if (*p < '0' || *p > '9') {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
start = start * 10 + *p++ - '0';
|
||||
}
|
||||
|
||||
while (*p == ' ') { p++; }
|
||||
|
||||
if (*p++ != '-') {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
while (*p == ' ') { p++; }
|
||||
|
||||
if (*p == ',' || *p == '\0') {
|
||||
end = content_length;
|
||||
goto found;
|
||||
}
|
||||
|
||||
} else {
|
||||
suffix = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9') {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
end = end * 10 + *p++ - '0';
|
||||
}
|
||||
|
||||
while (*p == ' ') { p++; }
|
||||
|
||||
if (*p != ',' && *p != '\0') {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
start = content_length - end;
|
||||
end = content_length - 1;
|
||||
}
|
||||
|
||||
if (end >= content_length) {
|
||||
end = content_length;
|
||||
|
||||
} else {
|
||||
end++;
|
||||
}
|
||||
|
||||
found:
|
||||
|
||||
if (start < end) {
|
||||
range = ngx_array_push(&ctx->ranges);
|
||||
if (range == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
range->start = start;
|
||||
range->end = end;
|
||||
|
||||
size += end - start;
|
||||
|
||||
if (ranges-- == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p++ != ',') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->ranges.nelts == 0) {
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
if (size > content_length) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_singlepart_header(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx)
|
||||
{
|
||||
ngx_table_elt_t *content_range;
|
||||
ngx_http_range_t *range;
|
||||
|
||||
content_range = ngx_list_push(&r->headers_out.headers);
|
||||
if (content_range == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.content_range = content_range;
|
||||
|
||||
content_range->hash = 1;
|
||||
ngx_str_set(&content_range->key, "Content-Range");
|
||||
|
||||
content_range->value.data = ngx_pnalloc(r->pool,
|
||||
sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
|
||||
if (content_range->value.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* "Content-Range: bytes SSSS-EEEE/TTTT" header */
|
||||
|
||||
range = ctx->ranges.elts;
|
||||
|
||||
content_range->value.len = ngx_sprintf(content_range->value.data,
|
||||
"bytes %O-%O/%O",
|
||||
range->start, range->end - 1,
|
||||
r->headers_out.content_length_n)
|
||||
- content_range->value.data;
|
||||
|
||||
r->headers_out.content_length_n = range->end - range->start;
|
||||
|
||||
if (r->headers_out.content_length) {
|
||||
r->headers_out.content_length->hash = 0;
|
||||
r->headers_out.content_length = NULL;
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_multipart_header(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx)
|
||||
{
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_http_range_t *range;
|
||||
ngx_atomic_uint_t boundary;
|
||||
|
||||
len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
|
||||
+ sizeof(CRLF "Content-Type: ") - 1
|
||||
+ r->headers_out.content_type.len
|
||||
+ sizeof(CRLF "Content-Range: bytes ") - 1;
|
||||
|
||||
if (r->headers_out.content_type_len == r->headers_out.content_type.len
|
||||
&& r->headers_out.charset.len)
|
||||
{
|
||||
len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
|
||||
}
|
||||
|
||||
ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
|
||||
if (ctx->boundary_header.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
boundary = ngx_next_temp_number(0);
|
||||
|
||||
/*
|
||||
* The boundary header of the range:
|
||||
* CRLF
|
||||
* "--0123456789" CRLF
|
||||
* "Content-Type: image/jpeg" CRLF
|
||||
* "Content-Range: bytes "
|
||||
*/
|
||||
|
||||
if (r->headers_out.content_type_len == r->headers_out.content_type.len
|
||||
&& r->headers_out.charset.len)
|
||||
{
|
||||
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
|
||||
CRLF "--%0muA" CRLF
|
||||
"Content-Type: %V; charset=%V" CRLF
|
||||
"Content-Range: bytes ",
|
||||
boundary,
|
||||
&r->headers_out.content_type,
|
||||
&r->headers_out.charset)
|
||||
- ctx->boundary_header.data;
|
||||
|
||||
} else if (r->headers_out.content_type.len) {
|
||||
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
|
||||
CRLF "--%0muA" CRLF
|
||||
"Content-Type: %V" CRLF
|
||||
"Content-Range: bytes ",
|
||||
boundary,
|
||||
&r->headers_out.content_type)
|
||||
- ctx->boundary_header.data;
|
||||
|
||||
} else {
|
||||
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
|
||||
CRLF "--%0muA" CRLF
|
||||
"Content-Range: bytes ",
|
||||
boundary)
|
||||
- ctx->boundary_header.data;
|
||||
}
|
||||
|
||||
r->headers_out.content_type.data =
|
||||
ngx_pnalloc(r->pool,
|
||||
sizeof("Content-Type: multipart/byteranges; boundary=") - 1
|
||||
+ NGX_ATOMIC_T_LEN);
|
||||
|
||||
if (r->headers_out.content_type.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.content_type_lowcase = NULL;
|
||||
|
||||
/* "Content-Type: multipart/byteranges; boundary=0123456789" */
|
||||
|
||||
r->headers_out.content_type.len =
|
||||
ngx_sprintf(r->headers_out.content_type.data,
|
||||
"multipart/byteranges; boundary=%0muA",
|
||||
boundary)
|
||||
- r->headers_out.content_type.data;
|
||||
|
||||
r->headers_out.content_type_len = r->headers_out.content_type.len;
|
||||
|
||||
r->headers_out.charset.len = 0;
|
||||
|
||||
/* the size of the last boundary CRLF "--0123456789--" CRLF */
|
||||
|
||||
len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
|
||||
|
||||
range = ctx->ranges.elts;
|
||||
for (i = 0; i < ctx->ranges.nelts; i++) {
|
||||
|
||||
/* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
|
||||
|
||||
range[i].content_range.data =
|
||||
ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
|
||||
|
||||
if (range[i].content_range.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
|
||||
"%O-%O/%O" CRLF CRLF,
|
||||
range[i].start, range[i].end - 1,
|
||||
r->headers_out.content_length_n)
|
||||
- range[i].content_range.data;
|
||||
|
||||
len += ctx->boundary_header.len + range[i].content_range.len
|
||||
+ (size_t) (range[i].end - range[i].start);
|
||||
}
|
||||
|
||||
r->headers_out.content_length_n = len;
|
||||
|
||||
if (r->headers_out.content_length) {
|
||||
r->headers_out.content_length->hash = 0;
|
||||
r->headers_out.content_length = NULL;
|
||||
}
|
||||
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_not_satisfiable(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_table_elt_t *content_range;
|
||||
|
||||
r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
|
||||
content_range = ngx_list_push(&r->headers_out.headers);
|
||||
if (content_range == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->headers_out.content_range = content_range;
|
||||
|
||||
content_range->hash = 1;
|
||||
ngx_str_set(&content_range->key, "Content-Range");
|
||||
|
||||
content_range->value.data = ngx_pnalloc(r->pool,
|
||||
sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
|
||||
if (content_range->value.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
content_range->value.len = ngx_sprintf(content_range->value.data,
|
||||
"bytes */%O",
|
||||
r->headers_out.content_length_n)
|
||||
- content_range->value.data;
|
||||
|
||||
ngx_http_clear_content_length(r);
|
||||
|
||||
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_http_range_filter_ctx_t *ctx;
|
||||
|
||||
if (in == NULL) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
if (ctx->ranges.nelts == 1) {
|
||||
return ngx_http_range_singlepart_body(r, ctx, in);
|
||||
}
|
||||
|
||||
/*
|
||||
* multipart ranges are supported only if whole body is in a single buffer
|
||||
*/
|
||||
|
||||
if (ngx_buf_special(in->buf)) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return ngx_http_range_multipart_body(r, ctx, in);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_test_overlapped(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
|
||||
{
|
||||
off_t start, last;
|
||||
ngx_buf_t *buf;
|
||||
ngx_uint_t i;
|
||||
ngx_http_range_t *range;
|
||||
|
||||
if (ctx->offset) {
|
||||
goto overlapped;
|
||||
}
|
||||
|
||||
buf = in->buf;
|
||||
|
||||
if (!buf->last_buf) {
|
||||
start = ctx->offset;
|
||||
last = ctx->offset + ngx_buf_size(buf);
|
||||
|
||||
range = ctx->ranges.elts;
|
||||
for (i = 0; i < ctx->ranges.nelts; i++) {
|
||||
if (start > range[i].start || last < range[i].end) {
|
||||
goto overlapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->offset = ngx_buf_size(buf);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
overlapped:
|
||||
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"range in overlapped buffers");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_singlepart_body(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
|
||||
{
|
||||
off_t start, last;
|
||||
ngx_buf_t *buf;
|
||||
ngx_chain_t *out, *cl, **ll;
|
||||
ngx_http_range_t *range;
|
||||
|
||||
out = NULL;
|
||||
ll = &out;
|
||||
range = ctx->ranges.elts;
|
||||
|
||||
for (cl = in; cl; cl = cl->next) {
|
||||
|
||||
buf = cl->buf;
|
||||
|
||||
start = ctx->offset;
|
||||
last = ctx->offset + ngx_buf_size(buf);
|
||||
|
||||
ctx->offset = last;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http range body buf: %O-%O", start, last);
|
||||
|
||||
if (ngx_buf_special(buf)) {
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range->end <= start || range->start >= last) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http range body skip");
|
||||
|
||||
if (buf->in_file) {
|
||||
buf->file_pos = buf->file_last;
|
||||
}
|
||||
|
||||
buf->pos = buf->last;
|
||||
buf->sync = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range->start > start) {
|
||||
|
||||
if (buf->in_file) {
|
||||
buf->file_pos += range->start - start;
|
||||
}
|
||||
|
||||
if (ngx_buf_in_memory(buf)) {
|
||||
buf->pos += (size_t) (range->start - start);
|
||||
}
|
||||
}
|
||||
|
||||
if (range->end <= last) {
|
||||
|
||||
if (buf->in_file) {
|
||||
buf->file_last -= last - range->end;
|
||||
}
|
||||
|
||||
if (ngx_buf_in_memory(buf)) {
|
||||
buf->last -= (size_t) (last - range->end);
|
||||
}
|
||||
|
||||
buf->last_buf = 1;
|
||||
*ll = cl;
|
||||
cl->next = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
if (out == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return ngx_http_next_body_filter(r, out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_multipart_body(ngx_http_request_t *r,
|
||||
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
|
||||
{
|
||||
ngx_buf_t *b, *buf;
|
||||
ngx_uint_t i;
|
||||
ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
|
||||
ngx_http_range_t *range;
|
||||
|
||||
ll = &out;
|
||||
buf = in->buf;
|
||||
range = ctx->ranges.elts;
|
||||
|
||||
for (i = 0; i < ctx->ranges.nelts; i++) {
|
||||
|
||||
/*
|
||||
* The boundary header of the range:
|
||||
* CRLF
|
||||
* "--0123456789" CRLF
|
||||
* "Content-Type: image/jpeg" CRLF
|
||||
* "Content-Range: bytes "
|
||||
*/
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->memory = 1;
|
||||
b->pos = ctx->boundary_header.data;
|
||||
b->last = ctx->boundary_header.data + ctx->boundary_header.len;
|
||||
|
||||
hcl = ngx_alloc_chain_link(r->pool);
|
||||
if (hcl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
hcl->buf = b;
|
||||
|
||||
|
||||
/* "SSSS-EEEE/TTTT" CRLF CRLF */
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->temporary = 1;
|
||||
b->pos = range[i].content_range.data;
|
||||
b->last = range[i].content_range.data + range[i].content_range.len;
|
||||
|
||||
rcl = ngx_alloc_chain_link(r->pool);
|
||||
if (rcl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rcl->buf = b;
|
||||
|
||||
|
||||
/* the range data */
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->in_file = buf->in_file;
|
||||
b->temporary = buf->temporary;
|
||||
b->memory = buf->memory;
|
||||
b->mmap = buf->mmap;
|
||||
b->file = buf->file;
|
||||
|
||||
if (buf->in_file) {
|
||||
b->file_pos = buf->file_pos + range[i].start;
|
||||
b->file_last = buf->file_pos + range[i].end;
|
||||
}
|
||||
|
||||
if (ngx_buf_in_memory(buf)) {
|
||||
b->pos = buf->pos + (size_t) range[i].start;
|
||||
b->last = buf->pos + (size_t) range[i].end;
|
||||
}
|
||||
|
||||
dcl = ngx_alloc_chain_link(r->pool);
|
||||
if (dcl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
dcl->buf = b;
|
||||
|
||||
*ll = hcl;
|
||||
hcl->next = rcl;
|
||||
rcl->next = dcl;
|
||||
ll = &dcl->next;
|
||||
}
|
||||
|
||||
/* the last boundary CRLF "--0123456789--" CRLF */
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->temporary = 1;
|
||||
b->last_buf = 1;
|
||||
|
||||
b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
|
||||
+ sizeof("--" CRLF) - 1);
|
||||
if (b->pos == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
|
||||
sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
|
||||
*b->last++ = '-'; *b->last++ = '-';
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
|
||||
hcl = ngx_alloc_chain_link(r->pool);
|
||||
if (hcl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
hcl->buf = b;
|
||||
hcl->next = NULL;
|
||||
|
||||
*ll = hcl;
|
||||
|
||||
return ngx_http_next_body_filter(r, out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_header_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_range_header_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_range_body_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_range_body_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_REALIP_XREALIP 0
|
||||
#define NGX_HTTP_REALIP_XFWD 1
|
||||
#define NGX_HTTP_REALIP_HEADER 2
|
||||
#define NGX_HTTP_REALIP_PROXY 3
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *from; /* array of ngx_cidr_t */
|
||||
ngx_uint_t type;
|
||||
ngx_uint_t hash;
|
||||
ngx_str_t header;
|
||||
ngx_flag_t recursive;
|
||||
} ngx_http_realip_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_connection_t *connection;
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
ngx_str_t addr_text;
|
||||
} ngx_http_realip_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
|
||||
ngx_addr_t *addr);
|
||||
static void ngx_http_realip_cleanup(void *data);
|
||||
static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_realip_commands[] = {
|
||||
|
||||
{ ngx_string("set_real_ip_from"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_realip_from,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("real_ip_header"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_realip,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("real_ip_recursive"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_realip_loc_conf_t, recursive),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_realip_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_realip_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_realip_create_loc_conf, /* create location configuration */
|
||||
ngx_http_realip_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_realip_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_realip_module_ctx, /* module context */
|
||||
ngx_http_realip_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_int_t
|
||||
ngx_http_realip_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_str_t *value;
|
||||
ngx_uint_t i, hash;
|
||||
ngx_addr_t addr;
|
||||
ngx_array_t *xfwd;
|
||||
ngx_list_part_t *part;
|
||||
ngx_table_elt_t *header;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_realip_ctx_t *ctx;
|
||||
ngx_http_realip_loc_conf_t *rlcf;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
|
||||
|
||||
if (ctx) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
|
||||
|
||||
if (rlcf->from == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
switch (rlcf->type) {
|
||||
|
||||
case NGX_HTTP_REALIP_XREALIP:
|
||||
|
||||
if (r->headers_in.x_real_ip == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
value = &r->headers_in.x_real_ip->value;
|
||||
xfwd = NULL;
|
||||
|
||||
break;
|
||||
|
||||
case NGX_HTTP_REALIP_XFWD:
|
||||
|
||||
xfwd = &r->headers_in.x_forwarded_for;
|
||||
|
||||
if (xfwd->elts == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
value = NULL;
|
||||
|
||||
break;
|
||||
|
||||
case NGX_HTTP_REALIP_PROXY:
|
||||
|
||||
value = &r->connection->proxy_protocol_addr;
|
||||
|
||||
if (value->len == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
xfwd = NULL;
|
||||
|
||||
break;
|
||||
|
||||
default: /* NGX_HTTP_REALIP_HEADER */
|
||||
|
||||
part = &r->headers_in.headers.part;
|
||||
header = part->elts;
|
||||
|
||||
hash = rlcf->hash;
|
||||
len = rlcf->header.len;
|
||||
p = rlcf->header.data;
|
||||
|
||||
for (i = 0; /* void */ ; i++) {
|
||||
|
||||
if (i >= part->nelts) {
|
||||
if (part->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
header = part->elts;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (hash == header[i].hash
|
||||
&& len == header[i].key.len
|
||||
&& ngx_strncmp(p, header[i].lowcase_key, len) == 0)
|
||||
{
|
||||
value = &header[i].value;
|
||||
xfwd = NULL;
|
||||
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
found:
|
||||
|
||||
c = r->connection;
|
||||
|
||||
addr.sockaddr = c->sockaddr;
|
||||
addr.socklen = c->socklen;
|
||||
/* addr.name = c->addr_text; */
|
||||
|
||||
if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
|
||||
rlcf->recursive)
|
||||
!= NGX_DECLINED)
|
||||
{
|
||||
return ngx_http_realip_set_addr(r, &addr);
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
|
||||
{
|
||||
size_t len;
|
||||
u_char *p;
|
||||
u_char text[NGX_SOCKADDR_STRLEN];
|
||||
ngx_connection_t *c;
|
||||
ngx_pool_cleanup_t *cln;
|
||||
ngx_http_realip_ctx_t *ctx;
|
||||
|
||||
cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
|
||||
if (cln == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx = cln->data;
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
|
||||
|
||||
c = r->connection;
|
||||
|
||||
len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
|
||||
NGX_SOCKADDR_STRLEN, 0);
|
||||
if (len == 0) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_pnalloc(c->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(p, text, len);
|
||||
|
||||
cln->handler = ngx_http_realip_cleanup;
|
||||
|
||||
ctx->connection = c;
|
||||
ctx->sockaddr = c->sockaddr;
|
||||
ctx->socklen = c->socklen;
|
||||
ctx->addr_text = c->addr_text;
|
||||
|
||||
c->sockaddr = addr->sockaddr;
|
||||
c->socklen = addr->socklen;
|
||||
c->addr_text.len = len;
|
||||
c->addr_text.data = p;
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_realip_cleanup(void *data)
|
||||
{
|
||||
ngx_http_realip_ctx_t *ctx = data;
|
||||
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = ctx->connection;
|
||||
|
||||
c->sockaddr = ctx->sockaddr;
|
||||
c->socklen = ctx->socklen;
|
||||
c->addr_text = ctx->addr_text;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_realip_loc_conf_t *rlcf = conf;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_str_t *value;
|
||||
ngx_cidr_t *cidr;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (rlcf->from == NULL) {
|
||||
rlcf->from = ngx_array_create(cf->pool, 2,
|
||||
sizeof(ngx_cidr_t));
|
||||
if (rlcf->from == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cidr = ngx_array_push(rlcf->from);
|
||||
if (cidr == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
if (ngx_strcmp(value[1].data, "unix:") == 0) {
|
||||
cidr->family = AF_UNIX;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
rc = ngx_ptocidr(&value[1], cidr);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
|
||||
&value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"low address bits of %V are meaningless", &value[1]);
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_realip_loc_conf_t *rlcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
|
||||
rlcf->type = NGX_HTTP_REALIP_XREALIP;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
|
||||
rlcf->type = NGX_HTTP_REALIP_XFWD;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
|
||||
rlcf->type = NGX_HTTP_REALIP_PROXY;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
rlcf->type = NGX_HTTP_REALIP_HEADER;
|
||||
rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
|
||||
rlcf->header = value[1];
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_realip_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->from = NULL;
|
||||
* conf->hash = 0;
|
||||
* conf->header = { 0, NULL };
|
||||
*/
|
||||
|
||||
conf->type = NGX_CONF_UNSET_UINT;
|
||||
conf->recursive = NGX_CONF_UNSET;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_realip_loc_conf_t *prev = parent;
|
||||
ngx_http_realip_loc_conf_t *conf = child;
|
||||
|
||||
if (conf->from == NULL) {
|
||||
conf->from = prev->from;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
|
||||
ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
|
||||
|
||||
if (conf->header.len == 0) {
|
||||
conf->hash = prev->hash;
|
||||
conf->header = prev->header;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_realip_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_realip_handler;
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_realip_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_combined_t hash;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_array_t *regex;
|
||||
ngx_array_t *server_name_regex;
|
||||
#endif
|
||||
|
||||
ngx_flag_t no_referer;
|
||||
ngx_flag_t blocked_referer;
|
||||
ngx_flag_t server_names;
|
||||
|
||||
ngx_hash_keys_arrays_t *keys;
|
||||
|
||||
ngx_uint_t referer_hash_max_size;
|
||||
ngx_uint_t referer_hash_bucket_size;
|
||||
} ngx_http_referer_conf_t;
|
||||
|
||||
|
||||
static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
|
||||
static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,
|
||||
ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);
|
||||
static ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,
|
||||
ngx_http_referer_conf_t *rlcf, ngx_str_t *name);
|
||||
#if (NGX_PCRE)
|
||||
static ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,
|
||||
ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);
|
||||
#endif
|
||||
static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
|
||||
const void *two);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_referer_commands[] = {
|
||||
|
||||
{ ngx_string("valid_referers"),
|
||||
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_valid_referers,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("referer_hash_max_size"),
|
||||
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("referer_hash_bucket_size"),
|
||||
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_referer_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_referer_create_conf, /* create location configuration */
|
||||
ngx_http_referer_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_referer_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_referer_module_ctx, /* module context */
|
||||
ngx_http_referer_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_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];
|
||||
#if (NGX_PCRE)
|
||||
ngx_int_t rc;
|
||||
ngx_str_t referer;
|
||||
#endif
|
||||
|
||||
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
|
||||
&& rlcf->server_name_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;
|
||||
len -= 7;
|
||||
goto valid_scheme;
|
||||
|
||||
} else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
|
||||
ref += 8;
|
||||
len -= 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;
|
||||
}
|
||||
|
||||
if (i == 256) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
buf[i] = ngx_tolower(*p);
|
||||
key = ngx_hash(key, buf[i++]);
|
||||
}
|
||||
|
||||
uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
|
||||
|
||||
if (uri) {
|
||||
goto uri;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
if (rlcf->server_name_regex) {
|
||||
referer.len = p - ref;
|
||||
referer.data = buf;
|
||||
|
||||
rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,
|
||||
r->connection->log);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
goto valid;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NGX_DECLINED */
|
||||
}
|
||||
|
||||
if (rlcf->regex) {
|
||||
referer.len = len;
|
||||
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 void *
|
||||
ngx_http_referer_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_referer_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->hash = { NULL };
|
||||
* conf->server_names = 0;
|
||||
* conf->keys = NULL;
|
||||
*/
|
||||
|
||||
#if (NGX_PCRE)
|
||||
conf->regex = NGX_CONF_UNSET_PTR;
|
||||
conf->server_name_regex = NGX_CONF_UNSET_PTR;
|
||||
#endif
|
||||
|
||||
conf->no_referer = NGX_CONF_UNSET;
|
||||
conf->blocked_referer = NGX_CONF_UNSET;
|
||||
conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
|
||||
conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_referer_conf_t *prev = parent;
|
||||
ngx_http_referer_conf_t *conf = child;
|
||||
|
||||
ngx_uint_t n;
|
||||
ngx_hash_init_t hash;
|
||||
ngx_http_server_name_t *sn;
|
||||
ngx_http_core_srv_conf_t *cscf;
|
||||
|
||||
if (conf->keys == NULL) {
|
||||
conf->hash = prev->hash;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->server_name_regex,
|
||||
prev->server_name_regex, NULL);
|
||||
#endif
|
||||
ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
|
||||
ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
|
||||
ngx_conf_merge_uint_value(conf->referer_hash_max_size,
|
||||
prev->referer_hash_max_size, 2048);
|
||||
ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
|
||||
prev->referer_hash_bucket_size, 64);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (conf->server_names == 1) {
|
||||
cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
|
||||
|
||||
sn = cscf->server_names.elts;
|
||||
for (n = 0; n < cscf->server_names.nelts; n++) {
|
||||
|
||||
#if (NGX_PCRE)
|
||||
if (sn[n].regex) {
|
||||
|
||||
if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((conf->no_referer == 1 || conf->blocked_referer == 1)
|
||||
&& conf->keys->keys.nelts == 0
|
||||
&& conf->keys->dns_wc_head.nelts == 0
|
||||
&& conf->keys->dns_wc_tail.nelts == 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"the \"none\" or \"blocked\" referers are specified "
|
||||
"in the \"valid_referers\" directive "
|
||||
"without any valid referer");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->referer_hash_max_size,
|
||||
prev->referer_hash_max_size, 2048);
|
||||
ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
|
||||
prev->referer_hash_bucket_size, 64);
|
||||
conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
|
||||
ngx_cacheline_size);
|
||||
|
||||
hash.key = ngx_hash_key_lc;
|
||||
hash.max_size = conf->referer_hash_max_size;
|
||||
hash.bucket_size = conf->referer_hash_bucket_size;
|
||||
hash.name = "referer_hash";
|
||||
hash.pool = cf->pool;
|
||||
|
||||
if (conf->keys->keys.nelts) {
|
||||
hash.hash = &conf->hash.hash;
|
||||
hash.temp_pool = NULL;
|
||||
|
||||
if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->keys->dns_wc_head.nelts) {
|
||||
|
||||
ngx_qsort(conf->keys->dns_wc_head.elts,
|
||||
(size_t) conf->keys->dns_wc_head.nelts,
|
||||
sizeof(ngx_hash_key_t),
|
||||
ngx_http_cmp_referer_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = cf->temp_pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
|
||||
conf->keys->dns_wc_head.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
if (conf->keys->dns_wc_tail.nelts) {
|
||||
|
||||
ngx_qsort(conf->keys->dns_wc_tail.elts,
|
||||
(size_t) conf->keys->dns_wc_tail.nelts,
|
||||
sizeof(ngx_hash_key_t),
|
||||
ngx_http_cmp_referer_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = cf->temp_pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
|
||||
conf->keys->dns_wc_tail.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
if (conf->no_referer == NGX_CONF_UNSET) {
|
||||
conf->no_referer = 0;
|
||||
}
|
||||
|
||||
if (conf->blocked_referer == NGX_CONF_UNSET) {
|
||||
conf->blocked_referer = 0;
|
||||
}
|
||||
|
||||
conf->keys = NULL;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_referer_conf_t *rlcf = conf;
|
||||
|
||||
u_char *p;
|
||||
ngx_str_t *value, uri, name;
|
||||
ngx_uint_t i;
|
||||
ngx_http_variable_t *var;
|
||||
|
||||
ngx_str_set(&name, "invalid_referer");
|
||||
|
||||
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_referer_variable;
|
||||
|
||||
if (rlcf->keys == NULL) {
|
||||
rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
|
||||
if (rlcf->keys == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rlcf->keys->pool = cf->pool;
|
||||
rlcf->keys->temp_pool = cf->pool;
|
||||
|
||||
if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
if (value[i].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid referer \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "none") == 0) {
|
||||
rlcf->no_referer = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "blocked") == 0) {
|
||||
rlcf->blocked_referer = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "server_names") == 0) {
|
||||
rlcf->server_names = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i].data[0] == '~') {
|
||||
if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_str_null(&uri);
|
||||
|
||||
p = (u_char *) ngx_strchr(value[i].data, '/');
|
||||
|
||||
if (p) {
|
||||
uri.len = (value[i].data + value[i].len) - p;
|
||||
uri.data = p;
|
||||
value[i].len = p - value[i].data;
|
||||
}
|
||||
|
||||
if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
|
||||
ngx_str_t *value, ngx_str_t *uri)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_str_t *u;
|
||||
|
||||
if (uri == NULL || uri->len == 0) {
|
||||
u = NGX_HTTP_REFERER_NO_URI_PART;
|
||||
|
||||
} else {
|
||||
u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
|
||||
if (u == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*u = *uri;
|
||||
}
|
||||
|
||||
rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid hostname or wildcard \"%V\"", value);
|
||||
}
|
||||
|
||||
if (rc == NGX_BUSY) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"conflicting parameter \"%V\"", value);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
|
||||
ngx_str_t *name)
|
||||
{
|
||||
#if (NGX_PCRE)
|
||||
ngx_regex_elt_t *re;
|
||||
ngx_regex_compile_t rc;
|
||||
u_char errstr[NGX_MAX_CONF_ERRSTR];
|
||||
|
||||
if (name->len == 1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rlcf->regex == NGX_CONF_UNSET_PTR) {
|
||||
rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
|
||||
if (rlcf->regex == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
re = ngx_array_push(rlcf->regex);
|
||||
if (re == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
name->len--;
|
||||
name->data++;
|
||||
|
||||
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
|
||||
|
||||
rc.pattern = *name;
|
||||
rc.pool = cf->pool;
|
||||
rc.options = NGX_REGEX_CASELESS;
|
||||
rc.err.len = NGX_MAX_CONF_ERRSTR;
|
||||
rc.err.data = errstr;
|
||||
|
||||
if (ngx_regex_compile(&rc) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
re->regex = rc.regex;
|
||||
re->name = name->data;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
#else
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the using of the regex \"%V\" requires PCRE library",
|
||||
name);
|
||||
|
||||
return NGX_ERROR;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
|
||||
ngx_http_regex_t *regex)
|
||||
{
|
||||
ngx_regex_elt_t *re;
|
||||
|
||||
if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {
|
||||
rlcf->server_name_regex = ngx_array_create(cf->pool, 2,
|
||||
sizeof(ngx_regex_elt_t));
|
||||
if (rlcf->server_name_regex == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
re = ngx_array_push(rlcf->server_name_regex);
|
||||
if (re == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
re->regex = regex->regex;
|
||||
re->name = regex->name.data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_cmp_referer_wildcards(const void *one, const void *two)
|
||||
{
|
||||
ngx_hash_key_t *first, *second;
|
||||
|
||||
first = (ngx_hash_key_t *) one;
|
||||
second = (ngx_hash_key_t *) two;
|
||||
|
||||
return ngx_dns_strcmp(first->key.data, second->key.data);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,368 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_md5.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_complex_value_t *variable;
|
||||
ngx_http_complex_value_t *md5;
|
||||
ngx_str_t secret;
|
||||
} ngx_http_secure_link_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t expires;
|
||||
} ngx_http_secure_link_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,
|
||||
ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
|
||||
uintptr_t data);
|
||||
static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_secure_link_commands[] = {
|
||||
|
||||
{ ngx_string("secure_link"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_set_complex_value_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_secure_link_conf_t, variable),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("secure_link_md5"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_set_complex_value_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_secure_link_conf_t, md5),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("secure_link_secret"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_secure_link_conf_t, secret),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_secure_link_module_ctx = {
|
||||
ngx_http_secure_link_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_secure_link_create_conf, /* create location configuration */
|
||||
ngx_http_secure_link_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_secure_link_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_secure_link_module_ctx, /* module context */
|
||||
ngx_http_secure_link_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_str_t ngx_http_secure_link_name = ngx_string("secure_link");
|
||||
static ngx_str_t ngx_http_secure_link_expires_name =
|
||||
ngx_string("secure_link_expires");
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_secure_link_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
u_char *p, *last;
|
||||
ngx_str_t val, hash;
|
||||
time_t expires;
|
||||
ngx_md5_t md5;
|
||||
ngx_http_secure_link_ctx_t *ctx;
|
||||
ngx_http_secure_link_conf_t *conf;
|
||||
u_char hash_buf[16], md5_buf[16];
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);
|
||||
|
||||
if (conf->secret.data) {
|
||||
return ngx_http_secure_link_old_variable(r, conf, v, data);
|
||||
}
|
||||
|
||||
if (conf->variable == NULL || conf->md5 == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"secure link: \"%V\"", &val);
|
||||
|
||||
last = val.data + val.len;
|
||||
|
||||
p = ngx_strlchr(val.data, last, ',');
|
||||
expires = 0;
|
||||
|
||||
if (p) {
|
||||
val.len = p++ - val.data;
|
||||
|
||||
expires = ngx_atotm(p, last - p);
|
||||
if (expires <= 0) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);
|
||||
|
||||
ctx->expires.len = last - p;
|
||||
ctx->expires.data = p;
|
||||
}
|
||||
|
||||
if (val.len > 24) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
hash.len = 16;
|
||||
hash.data = hash_buf;
|
||||
|
||||
if (ngx_decode_base64url(&hash, &val) != NGX_OK) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (hash.len != 16) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"secure link md5: \"%V\"", &val);
|
||||
|
||||
ngx_md5_init(&md5);
|
||||
ngx_md5_update(&md5, val.data, val.len);
|
||||
ngx_md5_final(md5_buf, &md5);
|
||||
|
||||
if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
|
||||
v->len = 1;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_secure_link_old_variable(ngx_http_request_t *r,
|
||||
ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
u_char *p, *start, *end, *last;
|
||||
size_t len;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t i;
|
||||
ngx_md5_t md5;
|
||||
u_char hash[16];
|
||||
|
||||
p = &r->unparsed_uri.data[1];
|
||||
last = r->unparsed_uri.data + r->unparsed_uri.len;
|
||||
|
||||
while (p < last) {
|
||||
if (*p++ == '/') {
|
||||
start = p;
|
||||
goto md5_start;
|
||||
}
|
||||
}
|
||||
|
||||
goto not_found;
|
||||
|
||||
md5_start:
|
||||
|
||||
while (p < last) {
|
||||
if (*p++ == '/') {
|
||||
end = p - 1;
|
||||
goto url_start;
|
||||
}
|
||||
}
|
||||
|
||||
goto not_found;
|
||||
|
||||
url_start:
|
||||
|
||||
len = last - p;
|
||||
|
||||
if (end - start != 32 || len == 0) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
ngx_md5_init(&md5);
|
||||
ngx_md5_update(&md5, p, len);
|
||||
ngx_md5_update(&md5, conf->secret.data, conf->secret.len);
|
||||
ngx_md5_final(hash, &md5);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
n = ngx_hextoi(&start[2 * i], 2);
|
||||
if (n == NGX_ERROR || n != hash[i]) {
|
||||
goto not_found;
|
||||
}
|
||||
}
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = p;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_secure_link_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);
|
||||
|
||||
if (ctx) {
|
||||
v->len = ctx->expires.len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = ctx->expires.data;
|
||||
|
||||
} else {
|
||||
v->not_found = 1;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_secure_link_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_secure_link_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->variable = NULL;
|
||||
* conf->md5 = NULL;
|
||||
* conf->secret = { 0, NULL };
|
||||
*/
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_secure_link_conf_t *prev = parent;
|
||||
ngx_http_secure_link_conf_t *conf = child;
|
||||
|
||||
if (conf->secret.data) {
|
||||
if (conf->variable || conf->md5) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"secure_link_secret\" cannot be mixed with "
|
||||
"\"secure_link\" and \"secure_link_md5\"");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (conf->variable == NULL) {
|
||||
conf->variable = prev->variable;
|
||||
}
|
||||
|
||||
if (conf->md5 == NULL) {
|
||||
conf->md5 = prev->md5;
|
||||
}
|
||||
|
||||
if (conf->variable == NULL && conf->md5 == NULL) {
|
||||
conf->secret = prev->secret;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_secure_link_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_variable_t *var;
|
||||
|
||||
var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_secure_link_variable;
|
||||
|
||||
var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_secure_link_expires_variable;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t percent;
|
||||
ngx_http_variable_value_t value;
|
||||
} ngx_http_split_clients_part_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_complex_value_t value;
|
||||
ngx_array_t parts;
|
||||
} ngx_http_split_clients_ctx_t;
|
||||
|
||||
|
||||
static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||
void *conf);
|
||||
|
||||
static ngx_command_t ngx_http_split_clients_commands[] = {
|
||||
|
||||
{ ngx_string("split_clients"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
|
||||
ngx_conf_split_clients_block,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_split_clients_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_split_clients_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_split_clients_module_ctx, /* module context */
|
||||
ngx_http_split_clients_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_int_t
|
||||
ngx_http_split_clients_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;
|
||||
|
||||
uint32_t hash;
|
||||
ngx_str_t val;
|
||||
ngx_uint_t i;
|
||||
ngx_http_split_clients_part_t *part;
|
||||
|
||||
*v = ngx_http_variable_null_value;
|
||||
|
||||
if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
hash = ngx_murmur_hash2(val.data, val.len);
|
||||
|
||||
part = ctx->parts.elts;
|
||||
|
||||
for (i = 0; i < ctx->parts.nelts; i++) {
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http split: %uD %uD", hash, part[i].percent);
|
||||
|
||||
if (hash < part[i].percent || part[i].percent == 0) {
|
||||
*v = part[i].value;
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
char *rv;
|
||||
uint32_t sum, last;
|
||||
ngx_str_t *value, name;
|
||||
ngx_uint_t i;
|
||||
ngx_conf_t save;
|
||||
ngx_http_variable_t *var;
|
||||
ngx_http_split_clients_ctx_t *ctx;
|
||||
ngx_http_split_clients_part_t *part;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &ctx->value;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name = value[2];
|
||||
|
||||
if (name.data[0] != '$') {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid variable name \"%V\"", &name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len--;
|
||||
name.data++;
|
||||
|
||||
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_split_clients_variable;
|
||||
var->data = (uintptr_t) ctx;
|
||||
|
||||
if (ngx_array_init(&ctx->parts, cf->pool, 2,
|
||||
sizeof(ngx_http_split_clients_part_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
save = *cf;
|
||||
cf->ctx = ctx;
|
||||
cf->handler = ngx_http_split_clients;
|
||||
cf->handler_conf = conf;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = save;
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
last = 0;
|
||||
part = ctx->parts.elts;
|
||||
|
||||
for (i = 0; i < ctx->parts.nelts; i++) {
|
||||
sum = part[i].percent ? sum + part[i].percent : 10000;
|
||||
if (sum > 10000) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"percent total is greater than 100%%");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (part[i].percent) {
|
||||
last += part[i].percent * (uint64_t) 0xffffffff / 10000;
|
||||
part[i].percent = last;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
{
|
||||
ngx_int_t n;
|
||||
ngx_str_t *value;
|
||||
ngx_http_split_clients_ctx_t *ctx;
|
||||
ngx_http_split_clients_part_t *part;
|
||||
|
||||
ctx = cf->ctx;
|
||||
value = cf->args->elts;
|
||||
|
||||
part = ngx_array_push(&ctx->parts);
|
||||
if (part == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (value[0].len == 1 && value[0].data[0] == '*') {
|
||||
part->percent = 0;
|
||||
|
||||
} else {
|
||||
if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
n = ngx_atofp(value[0].data, value[0].len - 1, 2);
|
||||
if (n == NGX_ERROR || n == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
part->percent = (uint32_t) n;
|
||||
}
|
||||
|
||||
part->value.len = value[1].len;
|
||||
part->value.valid = 1;
|
||||
part->value.no_cacheable = 0;
|
||||
part->value.not_found = 0;
|
||||
part->value.data = value[1].data;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid percent value \"%V\"", &value[0]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_SSI_FILTER_H_INCLUDED_
|
||||
#define _NGX_HTTP_SSI_FILTER_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_SSI_MAX_PARAMS 16
|
||||
|
||||
#define NGX_HTTP_SSI_COMMAND_LEN 32
|
||||
#define NGX_HTTP_SSI_PARAM_LEN 32
|
||||
#define NGX_HTTP_SSI_PARAMS_N 4
|
||||
|
||||
|
||||
#define NGX_HTTP_SSI_COND_IF 1
|
||||
#define NGX_HTTP_SSI_COND_ELSE 2
|
||||
|
||||
|
||||
#define NGX_HTTP_SSI_NO_ENCODING 0
|
||||
#define NGX_HTTP_SSI_URL_ENCODING 1
|
||||
#define NGX_HTTP_SSI_ENTITY_ENCODING 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_t hash;
|
||||
ngx_hash_keys_arrays_t commands;
|
||||
} ngx_http_ssi_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_buf_t *buf;
|
||||
|
||||
u_char *pos;
|
||||
u_char *copy_start;
|
||||
u_char *copy_end;
|
||||
|
||||
ngx_uint_t key;
|
||||
ngx_str_t command;
|
||||
ngx_array_t params;
|
||||
ngx_table_elt_t *param;
|
||||
ngx_table_elt_t params_array[NGX_HTTP_SSI_PARAMS_N];
|
||||
|
||||
ngx_chain_t *in;
|
||||
ngx_chain_t *out;
|
||||
ngx_chain_t **last_out;
|
||||
ngx_chain_t *busy;
|
||||
ngx_chain_t *free;
|
||||
|
||||
ngx_uint_t state;
|
||||
ngx_uint_t saved_state;
|
||||
size_t saved;
|
||||
size_t looked;
|
||||
|
||||
size_t value_len;
|
||||
|
||||
ngx_list_t *variables;
|
||||
ngx_array_t *blocks;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_uint_t ncaptures;
|
||||
int *captures;
|
||||
u_char *captures_data;
|
||||
#endif
|
||||
|
||||
unsigned conditional:2;
|
||||
unsigned encoding:2;
|
||||
unsigned block:1;
|
||||
unsigned output:1;
|
||||
unsigned output_chosen:1;
|
||||
|
||||
ngx_http_request_t *wait;
|
||||
void *value_buf;
|
||||
ngx_str_t timefmt;
|
||||
ngx_str_t errmsg;
|
||||
} ngx_http_ssi_ctx_t;
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
|
||||
ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_uint_t index;
|
||||
|
||||
unsigned mandatory:1;
|
||||
unsigned multiple:1;
|
||||
} ngx_http_ssi_param_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_http_ssi_command_pt handler;
|
||||
ngx_http_ssi_param_t *params;
|
||||
|
||||
unsigned conditional:2;
|
||||
unsigned block:1;
|
||||
unsigned flush:1;
|
||||
} ngx_http_ssi_command_t;
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_ssi_filter_module;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */
|
||||
@@ -0,0 +1,964 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
|
||||
ngx_pool_t *pool, ngx_str_t *s);
|
||||
|
||||
|
||||
#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
|
||||
#define NGX_DEFAULT_ECDH_CURVE "prime256v1"
|
||||
|
||||
#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1"
|
||||
|
||||
|
||||
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||
static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen, void *arg);
|
||||
#endif
|
||||
|
||||
#ifdef TLSEXT_TYPE_next_proto_neg
|
||||
static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **out, unsigned int *outlen, void *arg);
|
||||
#endif
|
||||
|
||||
static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
|
||||
static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
|
||||
static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
|
||||
static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
|
||||
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
|
||||
{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },
|
||||
{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },
|
||||
{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
|
||||
{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_ssl_verify[] = {
|
||||
{ ngx_string("off"), 0 },
|
||||
{ ngx_string("on"), 1 },
|
||||
{ ngx_string("optional"), 2 },
|
||||
{ ngx_string("optional_no_ca"), 3 },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
|
||||
{ ngx_string("ssl"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_http_ssl_enable,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, enable),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, certificate),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate_key"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_password_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_ssl_password_file,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_dhparam"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, dhparam),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_ecdh_curve"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, ecdh_curve),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_protocols"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_conf_set_bitmask_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, protocols),
|
||||
&ngx_http_ssl_protocols },
|
||||
|
||||
{ ngx_string("ssl_ciphers"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, ciphers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_buffer_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, buffer_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_verify_client"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, verify),
|
||||
&ngx_http_ssl_verify },
|
||||
|
||||
{ ngx_string("ssl_verify_depth"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, verify_depth),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_client_certificate"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_trusted_certificate"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_prefer_server_ciphers"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_cache"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_ssl_session_cache,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_tickets"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, session_tickets),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_ticket_key"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, session_ticket_keys),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_sec_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_crl"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, crl),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_stapling"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, stapling),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_stapling_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_stapling_responder"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_stapling_verify"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_ssl_module_ctx = {
|
||||
ngx_http_ssl_add_variables, /* preconfiguration */
|
||||
ngx_http_ssl_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_http_ssl_create_srv_conf, /* create server configuration */
|
||||
ngx_http_ssl_merge_srv_conf, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_ssl_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_ssl_module_ctx, /* module context */
|
||||
ngx_http_ssl_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_http_variable_t ngx_http_ssl_vars[] = {
|
||||
|
||||
{ ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,
|
||||
(uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
|
||||
(uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_reused"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_server_name"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_server_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_raw_certificate,
|
||||
NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_fingerprint"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_fingerprint, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
|
||||
|
||||
|
||||
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||
|
||||
static int
|
||||
ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
unsigned int srvlen;
|
||||
unsigned char *srv;
|
||||
#if (NGX_DEBUG)
|
||||
unsigned int i;
|
||||
#endif
|
||||
#if (NGX_HTTP_SPDY)
|
||||
ngx_http_connection_t *hc;
|
||||
#endif
|
||||
#if (NGX_HTTP_SPDY || NGX_DEBUG)
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
#endif
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
for (i = 0; i < inlen; i += in[i] + 1) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"SSL ALPN supported by client: %*s", in[i], &in[i + 1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_SPDY)
|
||||
hc = c->data;
|
||||
|
||||
if (hc->addr_conf->spdy) {
|
||||
srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
|
||||
srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
|
||||
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
|
||||
srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
|
||||
}
|
||||
|
||||
if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,
|
||||
in, inlen)
|
||||
!= OPENSSL_NPN_NEGOTIATED)
|
||||
{
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"SSL ALPN selected: %*s", *outlen, *out);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TLSEXT_TYPE_next_proto_neg
|
||||
|
||||
static int
|
||||
ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **out, unsigned int *outlen, void *arg)
|
||||
{
|
||||
#if (NGX_HTTP_SPDY || NGX_DEBUG)
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised");
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_SPDY)
|
||||
{
|
||||
ngx_http_connection_t *hc;
|
||||
|
||||
hc = c->data;
|
||||
|
||||
if (hc->addr_conf->spdy) {
|
||||
*out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
|
||||
*outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
|
||||
*outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ssl_static_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
|
||||
|
||||
size_t len;
|
||||
ngx_str_t s;
|
||||
|
||||
if (r->connection->ssl) {
|
||||
|
||||
(void) handler(r->connection, NULL, &s);
|
||||
|
||||
v->data = s.data;
|
||||
|
||||
for (len = 0; v->data[len]; len++) { /* void */ }
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
|
||||
|
||||
ngx_str_t s;
|
||||
|
||||
if (r->connection->ssl) {
|
||||
|
||||
if (handler(r->connection, r->pool, &s) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->len = s.len;
|
||||
v->data = s.data;
|
||||
|
||||
if (v->len) {
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ssl_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_http_ssl_vars; v->name.len; v++) {
|
||||
var = ngx_http_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf;
|
||||
|
||||
sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
|
||||
if (sscf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* sscf->protocols = 0;
|
||||
* sscf->certificate = { 0, NULL };
|
||||
* sscf->certificate_key = { 0, NULL };
|
||||
* sscf->dhparam = { 0, NULL };
|
||||
* sscf->ecdh_curve = { 0, NULL };
|
||||
* sscf->client_certificate = { 0, NULL };
|
||||
* sscf->trusted_certificate = { 0, NULL };
|
||||
* sscf->crl = { 0, NULL };
|
||||
* sscf->ciphers = { 0, NULL };
|
||||
* sscf->shm_zone = NULL;
|
||||
* sscf->stapling_file = { 0, NULL };
|
||||
* sscf->stapling_responder = { 0, NULL };
|
||||
*/
|
||||
|
||||
sscf->enable = NGX_CONF_UNSET;
|
||||
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
|
||||
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
sscf->verify = NGX_CONF_UNSET_UINT;
|
||||
sscf->verify_depth = NGX_CONF_UNSET_UINT;
|
||||
sscf->passwords = NGX_CONF_UNSET_PTR;
|
||||
sscf->builtin_session_cache = NGX_CONF_UNSET;
|
||||
sscf->session_timeout = NGX_CONF_UNSET;
|
||||
sscf->session_tickets = NGX_CONF_UNSET;
|
||||
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
|
||||
sscf->stapling = NGX_CONF_UNSET;
|
||||
sscf->stapling_verify = NGX_CONF_UNSET;
|
||||
|
||||
return sscf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *prev = parent;
|
||||
ngx_http_ssl_srv_conf_t *conf = child;
|
||||
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
if (conf->enable == NGX_CONF_UNSET) {
|
||||
if (prev->enable == NGX_CONF_UNSET) {
|
||||
conf->enable = 0;
|
||||
|
||||
} else {
|
||||
conf->enable = prev->enable;
|
||||
conf->file = prev->file;
|
||||
conf->line = prev->line;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_conf_merge_value(conf->session_timeout,
|
||||
prev->session_timeout, 300);
|
||||
|
||||
ngx_conf_merge_value(conf->prefer_server_ciphers,
|
||||
prev->prefer_server_ciphers, 0);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
|
||||
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
|
||||
|
||||
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
|
||||
NGX_SSL_BUFSIZE);
|
||||
|
||||
ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
|
||||
ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
|
||||
|
||||
ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
|
||||
ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
|
||||
|
||||
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
|
||||
"");
|
||||
ngx_conf_merge_str_value(conf->trusted_certificate,
|
||||
prev->trusted_certificate, "");
|
||||
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
|
||||
NGX_DEFAULT_ECDH_CURVE);
|
||||
|
||||
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
|
||||
|
||||
ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
|
||||
ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
|
||||
ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
|
||||
ngx_conf_merge_str_value(conf->stapling_responder,
|
||||
prev->stapling_responder, "");
|
||||
|
||||
conf->ssl.log = cf->log;
|
||||
|
||||
if (conf->enable) {
|
||||
|
||||
if (conf->certificate.len == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no \"ssl_certificate\" is defined for "
|
||||
"the \"ssl\" directive in %s:%ui",
|
||||
conf->file, conf->line);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->certificate_key.len == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no \"ssl_certificate_key\" is defined for "
|
||||
"the \"ssl\" directive in %s:%ui",
|
||||
conf->file, conf->line);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (conf->certificate.len == 0) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (conf->certificate_key.len == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no \"ssl_certificate_key\" is defined "
|
||||
"for certificate \"%V\"", &conf->certificate);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||
|
||||
if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
|
||||
ngx_http_ssl_servername)
|
||||
== 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
|
||||
"nginx was built with SNI support, however, now it is linked "
|
||||
"dynamically to an OpenSSL library which has no tlsext support, "
|
||||
"therefore SNI is not available");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||
SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_http_ssl_alpn_select, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef TLSEXT_TYPE_next_proto_neg
|
||||
SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx,
|
||||
ngx_http_ssl_npn_advertised, NULL);
|
||||
#endif
|
||||
|
||||
cln = ngx_pool_cleanup_add(cf->pool, 0);
|
||||
if (cln == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
cln->handler = ngx_ssl_cleanup_ctx;
|
||||
cln->data = &conf->ssl;
|
||||
|
||||
if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
|
||||
&conf->certificate_key, conf->passwords)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
|
||||
(const char *) conf->ciphers.data)
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"SSL_CTX_set_cipher_list(\"%V\") failed",
|
||||
&conf->ciphers);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
conf->ssl.buffer_size = conf->buffer_size;
|
||||
|
||||
if (conf->verify) {
|
||||
|
||||
if (conf->client_certificate.len == 0 && conf->verify != 3) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no ssl_client_certificate for ssl_client_verify");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_client_certificate(cf, &conf->ssl,
|
||||
&conf->client_certificate,
|
||||
conf->verify_depth)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
|
||||
&conf->trusted_certificate,
|
||||
conf->verify_depth)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->prefer_server_ciphers) {
|
||||
SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
}
|
||||
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
/* a temporary 512-bit RSA key is required for export versions of MSIE */
|
||||
SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
|
||||
#endif
|
||||
|
||||
if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_value(conf->builtin_session_cache,
|
||||
prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
|
||||
|
||||
if (conf->shm_zone == NULL) {
|
||||
conf->shm_zone = prev->shm_zone;
|
||||
}
|
||||
|
||||
if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
|
||||
conf->builtin_session_cache,
|
||||
conf->shm_zone, conf->session_timeout)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1);
|
||||
|
||||
#ifdef SSL_OP_NO_TICKET
|
||||
if (!conf->session_tickets) {
|
||||
SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
|
||||
}
|
||||
#endif
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->session_ticket_keys,
|
||||
prev->session_ticket_keys, NULL);
|
||||
|
||||
if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->stapling) {
|
||||
|
||||
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
|
||||
&conf->stapling_responder, conf->stapling_verify)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf = conf;
|
||||
|
||||
char *rv;
|
||||
|
||||
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
sscf->file = cf->conf_file->file.name.data;
|
||||
sscf->line = cf->conf_file->line;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (sscf->passwords != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
|
||||
|
||||
if (sscf->passwords == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf = conf;
|
||||
|
||||
size_t len;
|
||||
ngx_str_t *value, name, size;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t i, j;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strcmp(value[i].data, "off") == 0) {
|
||||
sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "none") == 0) {
|
||||
sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "builtin") == 0) {
|
||||
sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i].len > sizeof("builtin:") - 1
|
||||
&& ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
|
||||
== 0)
|
||||
{
|
||||
n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
|
||||
value[i].len - (sizeof("builtin:") - 1));
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
sscf->builtin_session_cache = n;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i].len > sizeof("shared:") - 1
|
||||
&& ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
|
||||
== 0)
|
||||
{
|
||||
len = 0;
|
||||
|
||||
for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
|
||||
if (value[i].data[j] == ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
name.len = len;
|
||||
name.data = value[i].data + sizeof("shared:") - 1;
|
||||
|
||||
size.len = value[i].len - j - 1;
|
||||
size.data = name.data + len + 1;
|
||||
|
||||
n = ngx_parse_size(&size);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (n < (ngx_int_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"session cache \"%V\" is too small",
|
||||
&value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
|
||||
&ngx_http_ssl_module);
|
||||
if (sscf->shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
sscf->shm_zone->init = ngx_ssl_session_cache_init;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
|
||||
sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid session cache \"%V\"", &value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ssl_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_uint_t s;
|
||||
ngx_http_ssl_srv_conf_t *sscf;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_core_srv_conf_t **cscfp;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
cscfp = cmcf->servers.elts;
|
||||
|
||||
for (s = 0; s < cmcf->servers.nelts; s++) {
|
||||
|
||||
sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
|
||||
|
||||
if (sscf->ssl.ctx == NULL || !sscf->stapling) {
|
||||
continue;
|
||||
}
|
||||
|
||||
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
|
||||
|
||||
if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
|
||||
clcf->resolver_timeout)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_SSL_H_INCLUDED_
|
||||
#define _NGX_HTTP_SSL_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_flag_t enable;
|
||||
|
||||
ngx_ssl_t ssl;
|
||||
|
||||
ngx_flag_t prefer_server_ciphers;
|
||||
|
||||
ngx_uint_t protocols;
|
||||
|
||||
ngx_uint_t verify;
|
||||
ngx_uint_t verify_depth;
|
||||
|
||||
size_t buffer_size;
|
||||
|
||||
ssize_t builtin_session_cache;
|
||||
|
||||
time_t session_timeout;
|
||||
|
||||
ngx_str_t certificate;
|
||||
ngx_str_t certificate_key;
|
||||
ngx_str_t dhparam;
|
||||
ngx_str_t ecdh_curve;
|
||||
ngx_str_t client_certificate;
|
||||
ngx_str_t trusted_certificate;
|
||||
ngx_str_t crl;
|
||||
|
||||
ngx_str_t ciphers;
|
||||
|
||||
ngx_array_t *passwords;
|
||||
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
|
||||
ngx_flag_t session_tickets;
|
||||
ngx_array_t *session_ticket_keys;
|
||||
|
||||
ngx_flag_t stapling;
|
||||
ngx_flag_t stapling_verify;
|
||||
ngx_str_t stapling_file;
|
||||
ngx_str_t stapling_responder;
|
||||
|
||||
u_char *file;
|
||||
ngx_uint_t line;
|
||||
} ngx_http_ssl_srv_conf_t;
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_ssl_module;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_SSL_H_INCLUDED_ */
|
||||
@@ -0,0 +1,290 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_static_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
ngx_http_module_t ngx_http_static_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_static_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_static_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_static_module_ctx, /* module context */
|
||||
NULL, /* 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_int_t
|
||||
ngx_http_static_handler(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *last, *location;
|
||||
size_t root, len;
|
||||
ngx_str_t path;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t level;
|
||||
ngx_log_t *log;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t out;
|
||||
ngx_open_file_info_t of;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
if (r->uri.data[r->uri.len - 1] == '/') {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
log = r->connection->log;
|
||||
|
||||
/*
|
||||
* ngx_http_map_uri_to_path() allocates memory for terminating '\0'
|
||||
* so we do not need to reserve memory for '/' for possible redirect
|
||||
*/
|
||||
|
||||
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, log, 0,
|
||||
"http filename: \"%s\"", path.data);
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
||||
|
||||
of.read_ahead = clcf->read_ahead;
|
||||
of.directio = clcf->directio;
|
||||
of.valid = clcf->open_file_cache_valid;
|
||||
of.min_uses = clcf->open_file_cache_min_uses;
|
||||
of.errors = clcf->open_file_cache_errors;
|
||||
of.events = clcf->open_file_cache_events;
|
||||
|
||||
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_open_cached_file(clcf->open_file_cache, &path, &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:
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
case NGX_EMLINK:
|
||||
case NGX_ELOOP:
|
||||
#endif
|
||||
|
||||
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 || clcf->log_not_found) {
|
||||
ngx_log_error(level, log, of.err,
|
||||
"%s \"%s\" failed", of.failed, path.data);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
r->root_tested = !r->error_page;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
|
||||
|
||||
if (of.is_dir) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
|
||||
|
||||
ngx_http_clear_location(r);
|
||||
|
||||
r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
|
||||
if (r->headers_out.location == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
len = r->uri.len + 1;
|
||||
|
||||
if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
|
||||
location = path.data + clcf->root.len;
|
||||
|
||||
*last = '/';
|
||||
|
||||
} else {
|
||||
if (r->args.len) {
|
||||
len += r->args.len + 1;
|
||||
}
|
||||
|
||||
location = ngx_pnalloc(r->pool, len);
|
||||
if (location == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
last = ngx_copy(location, r->uri.data, r->uri.len);
|
||||
|
||||
*last = '/';
|
||||
|
||||
if (r->args.len) {
|
||||
*++last = '?';
|
||||
ngx_memcpy(++last, r->args.data, r->args.len);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* we do not need to set the r->headers_out.location->hash and
|
||||
* r->headers_out.location->key fields
|
||||
*/
|
||||
|
||||
r->headers_out.location->value.len = len;
|
||||
r->headers_out.location->value.data = location;
|
||||
|
||||
return NGX_HTTP_MOVED_PERMANENTLY;
|
||||
}
|
||||
|
||||
#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
|
||||
|
||||
if (!of.is_file) {
|
||||
ngx_log_error(NGX_LOG_CRIT, log, 0,
|
||||
"\"%s\" is not a regular file", path.data);
|
||||
|
||||
return NGX_HTTP_NOT_FOUND;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (r->method & NGX_HTTP_POST) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
rc = ngx_http_discard_request_body(r);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
log->action = "sending response to client";
|
||||
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
r->headers_out.content_length_n = of.size;
|
||||
r->headers_out.last_modified_time = of.mtime;
|
||||
|
||||
if (ngx_http_set_etag(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_set_content_type(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (r != r->main && of.size == 0) {
|
||||
return ngx_http_send_header(r);
|
||||
}
|
||||
|
||||
r->allow_ranges = 1;
|
||||
|
||||
/* we need to allocate all before the header would be sent */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
b->file_pos = 0;
|
||||
b->file_last = of.size;
|
||||
|
||||
b->in_file = b->file_last ? 1: 0;
|
||||
b->last_buf = (r == r->main) ? 1: 0;
|
||||
b->last_in_chain = 1;
|
||||
|
||||
b->file->fd = of.fd;
|
||||
b->file->name = path;
|
||||
b->file->log = log;
|
||||
b->file->directio = of.is_directio;
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
return ngx_http_output_filter(r, &out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_static_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_http_static_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_stub_status_handler(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_stub_status_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_stub_status_add_variables(ngx_conf_t *cf);
|
||||
static char *ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_status_commands[] = {
|
||||
|
||||
{ ngx_string("stub_status"),
|
||||
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
|
||||
ngx_http_set_stub_status,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_stub_status_module_ctx = {
|
||||
ngx_http_stub_status_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_stub_status_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_stub_status_module_ctx, /* module context */
|
||||
ngx_http_status_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_http_variable_t ngx_http_stub_status_vars[] = {
|
||||
|
||||
{ ngx_string("connections_active"), NULL, ngx_http_stub_status_variable,
|
||||
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("connections_reading"), NULL, ngx_http_stub_status_variable,
|
||||
1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("connections_writing"), NULL, ngx_http_stub_status_variable,
|
||||
2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("connections_waiting"), NULL, ngx_http_stub_status_variable,
|
||||
3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_stub_status_handler(ngx_http_request_t *r)
|
||||
{
|
||||
size_t size;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t out;
|
||||
ngx_atomic_int_t ap, hn, ac, rq, rd, wr, wa;
|
||||
|
||||
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
|
||||
return NGX_HTTP_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
rc = ngx_http_discard_request_body(r);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
r->headers_out.content_type_len = sizeof("text/plain") - 1;
|
||||
ngx_str_set(&r->headers_out.content_type, "text/plain");
|
||||
r->headers_out.content_type_lowcase = NULL;
|
||||
|
||||
if (r->method == NGX_HTTP_HEAD) {
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
size = sizeof("Active connections: \n") + NGX_ATOMIC_T_LEN
|
||||
+ sizeof("server accepts handled requests\n") - 1
|
||||
+ 6 + 3 * NGX_ATOMIC_T_LEN
|
||||
+ sizeof("Reading: Writing: Waiting: \n") + 3 * NGX_ATOMIC_T_LEN;
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, size);
|
||||
if (b == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
ap = *ngx_stat_accepted;
|
||||
hn = *ngx_stat_handled;
|
||||
ac = *ngx_stat_active;
|
||||
rq = *ngx_stat_requests;
|
||||
rd = *ngx_stat_reading;
|
||||
wr = *ngx_stat_writing;
|
||||
wa = *ngx_stat_waiting;
|
||||
|
||||
b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac);
|
||||
|
||||
b->last = ngx_cpymem(b->last, "server accepts handled requests\n",
|
||||
sizeof("server accepts handled requests\n") - 1);
|
||||
|
||||
b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq);
|
||||
|
||||
b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n",
|
||||
rd, wr, wa);
|
||||
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
r->headers_out.content_length_n = b->last - b->pos;
|
||||
|
||||
b->last_buf = (r == r->main) ? 1 : 0;
|
||||
b->last_in_chain = 1;
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ngx_http_output_filter(r, &out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_stub_status_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
u_char *p;
|
||||
ngx_atomic_int_t value;
|
||||
|
||||
p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case 0:
|
||||
value = *ngx_stat_active;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = *ngx_stat_reading;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = *ngx_stat_writing;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = *ngx_stat_waiting;
|
||||
break;
|
||||
|
||||
/* suppress warning */
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
v->len = ngx_sprintf(p, "%uA", value) - p;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = p;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_stub_status_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_http_stub_status_vars; v->name.len; v++) {
|
||||
var = ngx_http_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_http_stub_status_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t match;
|
||||
ngx_http_complex_value_t value;
|
||||
|
||||
ngx_hash_t types;
|
||||
|
||||
ngx_flag_t once;
|
||||
ngx_flag_t last_modified;
|
||||
|
||||
ngx_array_t *types_keys;
|
||||
} ngx_http_sub_loc_conf_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
sub_start_state = 0,
|
||||
sub_match_state,
|
||||
} ngx_http_sub_state_e;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t match;
|
||||
ngx_str_t saved;
|
||||
ngx_str_t looked;
|
||||
|
||||
ngx_uint_t once; /* unsigned once:1 */
|
||||
|
||||
ngx_buf_t *buf;
|
||||
|
||||
u_char *pos;
|
||||
u_char *copy_start;
|
||||
u_char *copy_end;
|
||||
|
||||
ngx_chain_t *in;
|
||||
ngx_chain_t *out;
|
||||
ngx_chain_t **last_out;
|
||||
ngx_chain_t *busy;
|
||||
ngx_chain_t *free;
|
||||
|
||||
ngx_str_t sub;
|
||||
|
||||
ngx_uint_t state;
|
||||
} ngx_http_sub_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
|
||||
ngx_http_sub_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
|
||||
ngx_http_sub_ctx_t *ctx);
|
||||
|
||||
static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_sub_filter_commands[] = {
|
||||
|
||||
{ ngx_string("sub_filter"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
|
||||
ngx_http_sub_filter,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("sub_filter_types"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_http_types_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_sub_loc_conf_t, types_keys),
|
||||
&ngx_http_html_default_types[0] },
|
||||
|
||||
{ ngx_string("sub_filter_once"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_sub_loc_conf_t, once),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("sub_filter_last_modified"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_sub_loc_conf_t, last_modified),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_sub_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_sub_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_sub_create_conf, /* create location configuration */
|
||||
ngx_http_sub_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_sub_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_sub_filter_module_ctx, /* module context */
|
||||
ngx_http_sub_filter_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_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len);
|
||||
if (ctx->saved.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len);
|
||||
if (ctx->looked.data == 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);
|
||||
|
||||
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_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_sub_ctx_t *ctx;
|
||||
ngx_http_sub_loc_conf_t *slcf;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
if ((in == NULL
|
||||
&& ctx->buf == NULL
|
||||
&& ctx->in == NULL
|
||||
&& ctx->busy == NULL))
|
||||
{
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {
|
||||
|
||||
if (ctx->busy) {
|
||||
if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_http_next_body_filter(r, in);
|
||||
}
|
||||
|
||||
/* add the incoming chain to the chain ctx->in */
|
||||
|
||||
if (in) {
|
||||
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http sub filter \"%V\"", &r->uri);
|
||||
|
||||
while (ctx->in || ctx->buf) {
|
||||
|
||||
if (ctx->buf == NULL) {
|
||||
ctx->buf = ctx->in->buf;
|
||||
ctx->in = ctx->in->next;
|
||||
ctx->pos = ctx->buf->pos;
|
||||
}
|
||||
|
||||
if (ctx->state == sub_start_state) {
|
||||
ctx->copy_start = ctx->pos;
|
||||
ctx->copy_end = ctx->pos;
|
||||
}
|
||||
|
||||
b = NULL;
|
||||
|
||||
while (ctx->pos < ctx->buf->last) {
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"saved: \"%V\" state: %d", &ctx->saved, ctx->state);
|
||||
|
||||
rc = ngx_http_sub_parse(r, ctx);
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"parse: %d, looked: \"%V\" %p-%p",
|
||||
rc, &ctx->looked, ctx->copy_start, ctx->copy_end);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ctx->saved.len) {
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"saved: \"%V\"", &ctx->saved);
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
|
||||
if (b->pos == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
|
||||
b->last = b->pos + ctx->saved.len;
|
||||
b->memory = 1;
|
||||
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
ctx->saved.len = 0;
|
||||
}
|
||||
|
||||
if (ctx->copy_start != ctx->copy_end) {
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
|
||||
|
||||
b->pos = ctx->copy_start;
|
||||
b->last = ctx->copy_end;
|
||||
b->shadow = NULL;
|
||||
b->last_buf = 0;
|
||||
b->last_in_chain = 0;
|
||||
b->recycled = 0;
|
||||
|
||||
if (b->in_file) {
|
||||
b->file_last = b->file_pos + (b->last - ctx->buf->pos);
|
||||
b->file_pos += b->pos - ctx->buf->pos;
|
||||
}
|
||||
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
}
|
||||
|
||||
if (ctx->state == sub_start_state) {
|
||||
ctx->copy_start = ctx->pos;
|
||||
ctx->copy_end = ctx->pos;
|
||||
|
||||
} else {
|
||||
ctx->copy_start = NULL;
|
||||
ctx->copy_end = NULL;
|
||||
}
|
||||
|
||||
if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {
|
||||
ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);
|
||||
ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* rc == NGX_OK */
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
|
||||
|
||||
if (ctx->sub.data == NULL) {
|
||||
|
||||
if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->sub.len) {
|
||||
b->memory = 1;
|
||||
b->pos = ctx->sub.data;
|
||||
b->last = ctx->sub.data + ctx->sub.len;
|
||||
|
||||
} else {
|
||||
b->sync = 1;
|
||||
}
|
||||
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
ctx->once = slcf->once;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->looked.len
|
||||
&& (ctx->buf->last_buf || ctx->buf->last_in_chain))
|
||||
{
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->pos = ctx->looked.data;
|
||||
b->last = b->pos + ctx->looked.len;
|
||||
b->memory = 1;
|
||||
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
|
||||
ctx->looked.len = 0;
|
||||
}
|
||||
|
||||
if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
|
||||
|| ngx_buf_in_memory(ctx->buf))
|
||||
{
|
||||
if (b == NULL) {
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->sync = 1;
|
||||
|
||||
*ctx->last_out = cl;
|
||||
ctx->last_out = &cl->next;
|
||||
}
|
||||
|
||||
b->last_buf = ctx->buf->last_buf;
|
||||
b->last_in_chain = ctx->buf->last_in_chain;
|
||||
b->flush = ctx->buf->flush;
|
||||
b->shadow = ctx->buf;
|
||||
|
||||
b->recycled = ctx->buf->recycled;
|
||||
}
|
||||
|
||||
ctx->buf = NULL;
|
||||
|
||||
ctx->saved.len = ctx->looked.len;
|
||||
ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
|
||||
}
|
||||
|
||||
if (ctx->out == NULL && ctx->busy == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return ngx_http_sub_output(r, ctx);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
|
||||
#if 1
|
||||
b = NULL;
|
||||
for (cl = ctx->out; cl; cl = cl->next) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"sub out: %p %p", cl->buf, cl->buf->pos);
|
||||
if (cl->buf == b) {
|
||||
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
||||
"the same buf was used in sub");
|
||||
ngx_debug_point();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
b = cl->buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = ngx_http_next_body_filter(r, ctx->out);
|
||||
|
||||
if (ctx->busy == NULL) {
|
||||
ctx->busy = ctx->out;
|
||||
|
||||
} else {
|
||||
for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
|
||||
cl->next = ctx->out;
|
||||
}
|
||||
|
||||
ctx->out = NULL;
|
||||
ctx->last_out = &ctx->out;
|
||||
|
||||
while (ctx->busy) {
|
||||
|
||||
cl = ctx->busy;
|
||||
b = cl->buf;
|
||||
|
||||
if (ngx_buf_size(b) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (b->shadow) {
|
||||
b->shadow->pos = b->shadow->last;
|
||||
}
|
||||
|
||||
ctx->busy = cl->next;
|
||||
|
||||
if (ngx_buf_in_memory(b) || b->in_file) {
|
||||
/* add data bufs only to the free buf chain */
|
||||
|
||||
cl->next = ctx->free;
|
||||
ctx->free = cl;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->in || ctx->buf) {
|
||||
r->buffered |= NGX_HTTP_SUB_BUFFERED;
|
||||
|
||||
} else {
|
||||
r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
|
||||
{
|
||||
u_char *p, *last, *copy_end, ch, match;
|
||||
size_t looked, i;
|
||||
ngx_http_sub_state_e state;
|
||||
|
||||
if (ctx->once) {
|
||||
ctx->copy_start = ctx->pos;
|
||||
ctx->copy_end = ctx->buf->last;
|
||||
ctx->pos = ctx->buf->last;
|
||||
ctx->looked.len = 0;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once");
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
state = ctx->state;
|
||||
looked = ctx->looked.len;
|
||||
last = ctx->buf->last;
|
||||
copy_end = ctx->copy_end;
|
||||
|
||||
for (p = ctx->pos; p < last; p++) {
|
||||
|
||||
ch = *p;
|
||||
ch = ngx_tolower(ch);
|
||||
|
||||
if (state == sub_start_state) {
|
||||
|
||||
/* the tight loop */
|
||||
|
||||
match = ctx->match.data[0];
|
||||
|
||||
for ( ;; ) {
|
||||
if (ch == match) {
|
||||
|
||||
if (ctx->match.len == 1) {
|
||||
ctx->pos = p + 1;
|
||||
ctx->copy_end = p;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
copy_end = p;
|
||||
ctx->looked.data[0] = *p;
|
||||
looked = 1;
|
||||
state = sub_match_state;
|
||||
|
||||
goto match_started;
|
||||
}
|
||||
|
||||
if (++p == last) {
|
||||
break;
|
||||
}
|
||||
|
||||
ch = *p;
|
||||
ch = ngx_tolower(ch);
|
||||
}
|
||||
|
||||
ctx->state = state;
|
||||
ctx->pos = p;
|
||||
ctx->looked.len = looked;
|
||||
ctx->copy_end = p;
|
||||
|
||||
if (ctx->copy_start == NULL) {
|
||||
ctx->copy_start = ctx->buf->pos;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
|
||||
match_started:
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* state == sub_match_state */
|
||||
|
||||
if (ch == ctx->match.data[looked]) {
|
||||
ctx->looked.data[looked] = *p;
|
||||
looked++;
|
||||
|
||||
if (looked == ctx->match.len) {
|
||||
|
||||
ctx->state = sub_start_state;
|
||||
ctx->pos = p + 1;
|
||||
ctx->looked.len = 0;
|
||||
ctx->saved.len = 0;
|
||||
ctx->copy_end = copy_end;
|
||||
|
||||
if (ctx->copy_start == NULL && copy_end) {
|
||||
ctx->copy_start = ctx->buf->pos;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* check if there is another partial match in previously
|
||||
* matched substring to catch cases like "aab" in "aaab"
|
||||
*/
|
||||
|
||||
ctx->looked.data[looked] = *p;
|
||||
looked++;
|
||||
|
||||
for (i = 1; i < looked; i++) {
|
||||
if (ngx_strncasecmp(ctx->looked.data + i,
|
||||
ctx->match.data, looked - i)
|
||||
== 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < looked) {
|
||||
if (ctx->saved.len > i) {
|
||||
ctx->saved.len = i;
|
||||
}
|
||||
|
||||
if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) {
|
||||
copy_end = p + 1 - (looked - i);
|
||||
}
|
||||
|
||||
ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i);
|
||||
looked = looked - i;
|
||||
|
||||
} else {
|
||||
copy_end = p;
|
||||
looked = 0;
|
||||
state = sub_start_state;
|
||||
}
|
||||
|
||||
if (ctx->saved.len) {
|
||||
p++;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->saved.len = 0;
|
||||
|
||||
out:
|
||||
|
||||
ctx->state = state;
|
||||
ctx->pos = p;
|
||||
ctx->looked.len = looked;
|
||||
|
||||
ctx->copy_end = (state == sub_start_state) ? p : copy_end;
|
||||
|
||||
if (ctx->copy_start == NULL && ctx->copy_end) {
|
||||
ctx->copy_start = ctx->buf->pos;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_sub_loc_conf_t *slcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
if (slcf->match.data) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_strlow(value[1].data, value[1].data, value[1].len);
|
||||
|
||||
slcf->match = value[1];
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[2];
|
||||
ccv.complex_value = &slcf->value;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_sub_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_sub_loc_conf_t *slcf;
|
||||
|
||||
slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
|
||||
if (slcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->match = { 0, NULL };
|
||||
* conf->types = { NULL };
|
||||
* conf->types_keys = NULL;
|
||||
*/
|
||||
|
||||
slcf->once = NGX_CONF_UNSET;
|
||||
slcf->last_modified = NGX_CONF_UNSET;
|
||||
|
||||
return slcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_sub_loc_conf_t *prev = parent;
|
||||
ngx_http_sub_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_value(conf->once, prev->once, 1);
|
||||
ngx_conf_merge_str_value(conf->match, prev->match, "");
|
||||
ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
|
||||
|
||||
if (conf->value.value.data == NULL) {
|
||||
conf->value = prev->value;
|
||||
}
|
||||
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_sub_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_sub_header_filter;
|
||||
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_sub_body_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
ngx_str_t *server;
|
||||
} ngx_http_upstream_chash_point_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t number;
|
||||
ngx_http_upstream_chash_point_t point[1];
|
||||
} ngx_http_upstream_chash_points_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_complex_value_t key;
|
||||
ngx_http_upstream_chash_points_t *points;
|
||||
} ngx_http_upstream_hash_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
ngx_http_upstream_rr_peer_data_t rrp;
|
||||
ngx_http_upstream_hash_srv_conf_t *conf;
|
||||
ngx_str_t key;
|
||||
ngx_uint_t tries;
|
||||
ngx_uint_t rehash;
|
||||
uint32_t hash;
|
||||
ngx_event_get_peer_pt get_rr_peer;
|
||||
} ngx_http_upstream_hash_peer_data_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_upstream_chash_cmp_points(const void *one, const void *two);
|
||||
static ngx_uint_t ngx_http_upstream_find_chash_point(
|
||||
ngx_http_upstream_chash_points_t *points, uint32_t hash);
|
||||
static ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
static void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_upstream_hash_commands[] = {
|
||||
|
||||
{ ngx_string("hash"),
|
||||
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
|
||||
ngx_http_upstream_hash,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_upstream_hash_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_http_upstream_hash_create_conf, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_upstream_hash_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_upstream_hash_module_ctx, /* module context */
|
||||
ngx_http_upstream_hash_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_int_t
|
||||
ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_hash_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_http_upstream_hash_srv_conf_t *hcf;
|
||||
ngx_http_upstream_hash_peer_data_t *hp;
|
||||
|
||||
hp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t));
|
||||
if (hp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.data = &hp->rrp;
|
||||
|
||||
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_hash_peer;
|
||||
|
||||
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
|
||||
|
||||
if (ngx_http_complex_value(r, &hcf->key, &hp->key) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"upstream hash key:\"%V\"", &hp->key);
|
||||
|
||||
hp->conf = hcf;
|
||||
hp->tries = 0;
|
||||
hp->rehash = 0;
|
||||
hp->hash = 0;
|
||||
hp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_hash_peer_data_t *hp = data;
|
||||
|
||||
time_t now;
|
||||
u_char buf[NGX_INT_T_LEN];
|
||||
size_t size;
|
||||
uint32_t hash;
|
||||
ngx_int_t w;
|
||||
uintptr_t m;
|
||||
ngx_uint_t i, n, p;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get hash peer, try: %ui", pc->tries);
|
||||
|
||||
if (hp->tries > 20 || hp->rrp.peers->single) {
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
/*
|
||||
* Hash expression is compatible with Cache::Memcached:
|
||||
* ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
|
||||
* with REHASH omitted at the first iteration.
|
||||
*/
|
||||
|
||||
ngx_crc32_init(hash);
|
||||
|
||||
if (hp->rehash > 0) {
|
||||
size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
|
||||
ngx_crc32_update(&hash, buf, size);
|
||||
}
|
||||
|
||||
ngx_crc32_update(&hash, hp->key.data, hp->key.len);
|
||||
ngx_crc32_final(hash);
|
||||
|
||||
hash = (hash >> 16) & 0x7fff;
|
||||
|
||||
hp->hash += hash;
|
||||
hp->rehash++;
|
||||
|
||||
if (!hp->rrp.peers->weighted) {
|
||||
p = hp->hash % hp->rrp.peers->number;
|
||||
|
||||
} else {
|
||||
w = hp->hash % hp->rrp.peers->total_weight;
|
||||
|
||||
for (i = 0; i < hp->rrp.peers->number; i++) {
|
||||
w -= hp->rrp.peers->peer[i].weight;
|
||||
if (w < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p = i;
|
||||
}
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (hp->rrp.tried[n] & m) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get hash peer, value:%uD, peer:%ui", hp->hash, p);
|
||||
|
||||
peer = &hp->rrp.peers->peer[p];
|
||||
|
||||
if (peer->down) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
goto next;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
next:
|
||||
|
||||
if (++hp->tries > 20) {
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
}
|
||||
|
||||
hp->rrp.current = p;
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
pc->name = &peer->name;
|
||||
|
||||
if (now - peer->checked > peer->fail_timeout) {
|
||||
peer->checked = now;
|
||||
}
|
||||
|
||||
hp->rrp.tried[n] |= m;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
u_char *host, *port, c;
|
||||
size_t host_len, port_len, size;
|
||||
uint32_t hash, base_hash, prev_hash;
|
||||
ngx_str_t *server;
|
||||
ngx_uint_t npoints, i, j;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
ngx_http_upstream_chash_points_t *points;
|
||||
ngx_http_upstream_hash_srv_conf_t *hcf;
|
||||
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_chash_peer;
|
||||
|
||||
peers = us->peer.data;
|
||||
npoints = peers->total_weight * 160;
|
||||
|
||||
size = sizeof(ngx_http_upstream_chash_points_t)
|
||||
+ sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1);
|
||||
|
||||
points = ngx_palloc(cf->pool, size);
|
||||
if (points == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
points->number = 0;
|
||||
|
||||
for (i = 0; i < peers->number; i++) {
|
||||
peer = &peers->peer[i];
|
||||
server = &peer->server;
|
||||
|
||||
/*
|
||||
* Hash expression is compatible with Cache::Memcached::Fast:
|
||||
* crc32(HOST \0 PORT PREV_HASH).
|
||||
*/
|
||||
|
||||
if (server->len >= 5
|
||||
&& ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
|
||||
{
|
||||
host = server->data + 5;
|
||||
host_len = server->len - 5;
|
||||
port = NULL;
|
||||
port_len = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (j = 0; j < server->len; j++) {
|
||||
c = server->data[server->len - j - 1];
|
||||
|
||||
if (c == ':') {
|
||||
host = server->data;
|
||||
host_len = server->len - j - 1;
|
||||
port = server->data + server->len - j;
|
||||
port_len = j;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
host = server->data;
|
||||
host_len = server->len;
|
||||
port = NULL;
|
||||
port_len = 0;
|
||||
|
||||
done:
|
||||
|
||||
ngx_crc32_init(base_hash);
|
||||
ngx_crc32_update(&base_hash, host, host_len);
|
||||
ngx_crc32_update(&base_hash, (u_char *) "", 1);
|
||||
ngx_crc32_update(&base_hash, port, port_len);
|
||||
|
||||
prev_hash = 0;
|
||||
npoints = peer->weight * 160;
|
||||
|
||||
for (j = 0; j < npoints; j++) {
|
||||
hash = base_hash;
|
||||
|
||||
ngx_crc32_update(&hash, (u_char *) &prev_hash, sizeof(uint32_t));
|
||||
ngx_crc32_final(hash);
|
||||
|
||||
points->point[points->number].hash = hash;
|
||||
points->point[points->number].server = server;
|
||||
points->number++;
|
||||
|
||||
prev_hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_qsort(points->point,
|
||||
points->number,
|
||||
sizeof(ngx_http_upstream_chash_point_t),
|
||||
ngx_http_upstream_chash_cmp_points);
|
||||
|
||||
for (i = 0, j = 1; j < points->number; j++) {
|
||||
if (points->point[i].hash != points->point[j].hash) {
|
||||
points->point[++i] = points->point[j];
|
||||
}
|
||||
}
|
||||
|
||||
points->number = i + 1;
|
||||
|
||||
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
|
||||
hcf->points = points;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_upstream_chash_cmp_points(const void *one, const void *two)
|
||||
{
|
||||
ngx_http_upstream_chash_point_t *first =
|
||||
(ngx_http_upstream_chash_point_t *) one;
|
||||
ngx_http_upstream_chash_point_t *second =
|
||||
(ngx_http_upstream_chash_point_t *) two;
|
||||
|
||||
if (first->hash < second->hash) {
|
||||
return -1;
|
||||
|
||||
} else if (first->hash > second->hash) {
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,
|
||||
uint32_t hash)
|
||||
{
|
||||
ngx_uint_t i, j, k;
|
||||
ngx_http_upstream_chash_point_t *point;
|
||||
|
||||
/* find first point >= hash */
|
||||
|
||||
point = &points->point[0];
|
||||
|
||||
i = 0;
|
||||
j = points->number;
|
||||
|
||||
while (i < j) {
|
||||
k = (i + j) / 2;
|
||||
|
||||
if (hash > point[k].hash) {
|
||||
i = k + 1;
|
||||
|
||||
} else if (hash < point[k].hash) {
|
||||
j = k;
|
||||
|
||||
} else {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
uint32_t hash;
|
||||
ngx_http_upstream_hash_srv_conf_t *hcf;
|
||||
ngx_http_upstream_hash_peer_data_t *hp;
|
||||
|
||||
if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_chash_peer;
|
||||
|
||||
hp = r->upstream->peer.data;
|
||||
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
|
||||
|
||||
hash = ngx_crc32_long(hp->key.data, hp->key.len);
|
||||
hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_hash_peer_data_t *hp = data;
|
||||
|
||||
time_t now;
|
||||
intptr_t m;
|
||||
ngx_str_t *server;
|
||||
ngx_int_t total;
|
||||
ngx_uint_t i, n;
|
||||
ngx_http_upstream_rr_peer_t *peer, *best;
|
||||
ngx_http_upstream_chash_point_t *point;
|
||||
ngx_http_upstream_chash_points_t *points;
|
||||
ngx_http_upstream_hash_srv_conf_t *hcf;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get consistent hash peer, try: %ui", pc->tries);
|
||||
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
now = ngx_time();
|
||||
hcf = hp->conf;
|
||||
|
||||
points = hcf->points;
|
||||
point = &points->point[0];
|
||||
|
||||
for ( ;; ) {
|
||||
server = point[hp->hash % points->number].server;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"consistent hash peer:%uD, server:\"%V\"",
|
||||
hp->hash, server);
|
||||
|
||||
best = NULL;
|
||||
total = 0;
|
||||
|
||||
for (i = 0; i < hp->rrp.peers->number; i++) {
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (hp->rrp.tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer = &hp->rrp.peers->peer[i];
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (best == NULL || peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
}
|
||||
}
|
||||
|
||||
if (best) {
|
||||
best->current_weight -= total;
|
||||
|
||||
i = best - &hp->rrp.peers->peer[0];
|
||||
|
||||
hp->rrp.current = i;
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
hp->rrp.tried[n] |= m;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
pc->sockaddr = best->sockaddr;
|
||||
pc->socklen = best->socklen;
|
||||
pc->name = &best->name;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
hp->hash++;
|
||||
hp->tries++;
|
||||
|
||||
if (hp->tries >= points->number) {
|
||||
return NGX_BUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_upstream_hash_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_upstream_hash_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->points = NULL;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_upstream_hash_srv_conf_t *hcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_http_upstream_srv_conf_t *uscf;
|
||||
ngx_http_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &hcf->key;
|
||||
|
||||
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
|
||||
|
||||
if (uscf->peer.init_upstream) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"load balancing method redefined");
|
||||
}
|
||||
|
||||
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|
||||
|NGX_HTTP_UPSTREAM_WEIGHT
|
||||
|NGX_HTTP_UPSTREAM_MAX_FAILS
|
||||
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_HTTP_UPSTREAM_DOWN;
|
||||
|
||||
if (cf->args->nelts == 2) {
|
||||
uscf->peer.init_upstream = ngx_http_upstream_init_hash;
|
||||
|
||||
} else if (ngx_strcmp(value[2].data, "consistent") == 0) {
|
||||
uscf->peer.init_upstream = ngx_http_upstream_init_chash;
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
ngx_http_upstream_rr_peer_data_t rrp;
|
||||
|
||||
ngx_uint_t hash;
|
||||
|
||||
u_char addrlen;
|
||||
u_char *addr;
|
||||
|
||||
u_char tries;
|
||||
|
||||
ngx_event_get_peer_pt get_rr_peer;
|
||||
} ngx_http_upstream_ip_hash_peer_data_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_upstream_ip_hash_commands[] = {
|
||||
|
||||
{ ngx_string("ip_hash"),
|
||||
NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
|
||||
ngx_http_upstream_ip_hash,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_upstream_ip_hash_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_upstream_ip_hash_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_upstream_ip_hash_module_ctx, /* module context */
|
||||
ngx_http_upstream_ip_hash_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 u_char ngx_http_upstream_ip_hash_pseudo_addr[3];
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_ip_hash_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
ngx_http_upstream_ip_hash_peer_data_t *iphp;
|
||||
|
||||
iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
|
||||
if (iphp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.data = &iphp->rrp;
|
||||
|
||||
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
|
||||
|
||||
switch (r->connection->sockaddr->sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
sin = (struct sockaddr_in *) r->connection->sockaddr;
|
||||
iphp->addr = (u_char *) &sin->sin_addr.s_addr;
|
||||
iphp->addrlen = 3;
|
||||
break;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
|
||||
iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;
|
||||
iphp->addrlen = 16;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;
|
||||
iphp->addrlen = 3;
|
||||
}
|
||||
|
||||
iphp->hash = 89;
|
||||
iphp->tries = 0;
|
||||
iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_ip_hash_peer_data_t *iphp = data;
|
||||
|
||||
time_t now;
|
||||
ngx_int_t w;
|
||||
uintptr_t m;
|
||||
ngx_uint_t i, n, p, hash;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get ip hash peer, try: %ui", pc->tries);
|
||||
|
||||
/* TODO: cached */
|
||||
|
||||
if (iphp->tries > 20 || iphp->rrp.peers->single) {
|
||||
return iphp->get_rr_peer(pc, &iphp->rrp);
|
||||
}
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
hash = iphp->hash;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {
|
||||
hash = (hash * 113 + iphp->addr[i]) % 6271;
|
||||
}
|
||||
|
||||
if (!iphp->rrp.peers->weighted) {
|
||||
p = hash % iphp->rrp.peers->number;
|
||||
|
||||
} else {
|
||||
w = hash % iphp->rrp.peers->total_weight;
|
||||
|
||||
for (i = 0; i < iphp->rrp.peers->number; i++) {
|
||||
w -= iphp->rrp.peers->peer[i].weight;
|
||||
if (w < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p = i;
|
||||
}
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (iphp->rrp.tried[n] & m) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get ip hash peer, hash: %ui %04XA", p, m);
|
||||
|
||||
peer = &iphp->rrp.peers->peer[p];
|
||||
|
||||
/* ngx_lock_mutex(iphp->rrp.peers->mutex); */
|
||||
|
||||
if (peer->down) {
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
next_try:
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
|
||||
/* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
|
||||
|
||||
pc->tries--;
|
||||
|
||||
next:
|
||||
|
||||
if (++iphp->tries > 20) {
|
||||
return iphp->get_rr_peer(pc, &iphp->rrp);
|
||||
}
|
||||
}
|
||||
|
||||
iphp->rrp.current = p;
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
pc->name = &peer->name;
|
||||
|
||||
if (now - peer->checked > peer->fail_timeout) {
|
||||
peer->checked = now;
|
||||
}
|
||||
|
||||
/* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
|
||||
|
||||
iphp->rrp.tried[n] |= m;
|
||||
iphp->hash = hash;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_upstream_srv_conf_t *uscf;
|
||||
|
||||
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
|
||||
|
||||
if (uscf->peer.init_upstream) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"load balancing method redefined");
|
||||
}
|
||||
|
||||
uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
|
||||
|
||||
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|
||||
|NGX_HTTP_UPSTREAM_WEIGHT
|
||||
|NGX_HTTP_UPSTREAM_MAX_FAILS
|
||||
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_HTTP_UPSTREAM_DOWN;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t max_cached;
|
||||
|
||||
ngx_queue_t cache;
|
||||
ngx_queue_t free;
|
||||
|
||||
ngx_http_upstream_init_pt original_init_upstream;
|
||||
ngx_http_upstream_init_peer_pt original_init_peer;
|
||||
|
||||
} ngx_http_upstream_keepalive_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_keepalive_srv_conf_t *conf;
|
||||
|
||||
ngx_http_upstream_t *upstream;
|
||||
|
||||
void *data;
|
||||
|
||||
ngx_event_get_peer_pt original_get_peer;
|
||||
ngx_event_free_peer_pt original_free_peer;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_event_set_peer_session_pt original_set_session;
|
||||
ngx_event_save_peer_session_pt original_save_session;
|
||||
#endif
|
||||
|
||||
} ngx_http_upstream_keepalive_peer_data_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_keepalive_srv_conf_t *conf;
|
||||
|
||||
ngx_queue_t queue;
|
||||
ngx_connection_t *connection;
|
||||
|
||||
socklen_t socklen;
|
||||
u_char sockaddr[NGX_SOCKADDRLEN];
|
||||
|
||||
} ngx_http_upstream_keepalive_cache_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t state);
|
||||
|
||||
static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
|
||||
static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
|
||||
static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
|
||||
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
static ngx_int_t ngx_http_upstream_keepalive_set_session(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
#endif
|
||||
|
||||
static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_upstream_keepalive_commands[] = {
|
||||
|
||||
{ ngx_string("keepalive"),
|
||||
NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_upstream_keepalive,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_http_upstream_keepalive_create_conf, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_upstream_keepalive_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_upstream_keepalive_module_ctx, /* module context */
|
||||
ngx_http_upstream_keepalive_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_int_t
|
||||
ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_http_upstream_keepalive_srv_conf_t *kcf;
|
||||
ngx_http_upstream_keepalive_cache_t *cached;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
|
||||
"init keepalive");
|
||||
|
||||
kcf = ngx_http_conf_upstream_srv_conf(us,
|
||||
ngx_http_upstream_keepalive_module);
|
||||
|
||||
if (kcf->original_init_upstream(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
kcf->original_init_peer = us->peer.init;
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_keepalive_peer;
|
||||
|
||||
/* allocate cache items and add to free queue */
|
||||
|
||||
cached = ngx_pcalloc(cf->pool,
|
||||
sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
|
||||
if (cached == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_queue_init(&kcf->cache);
|
||||
ngx_queue_init(&kcf->free);
|
||||
|
||||
for (i = 0; i < kcf->max_cached; i++) {
|
||||
ngx_queue_insert_head(&kcf->free, &cached[i].queue);
|
||||
cached[i].conf = kcf;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_http_upstream_keepalive_peer_data_t *kp;
|
||||
ngx_http_upstream_keepalive_srv_conf_t *kcf;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"init keepalive peer");
|
||||
|
||||
kcf = ngx_http_conf_upstream_srv_conf(us,
|
||||
ngx_http_upstream_keepalive_module);
|
||||
|
||||
kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
|
||||
if (kp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (kcf->original_init_peer(r, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
kp->conf = kcf;
|
||||
kp->upstream = r->upstream;
|
||||
kp->data = r->upstream->peer.data;
|
||||
kp->original_get_peer = r->upstream->peer.get;
|
||||
kp->original_free_peer = r->upstream->peer.free;
|
||||
|
||||
r->upstream->peer.data = kp;
|
||||
r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
|
||||
r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
kp->original_set_session = r->upstream->peer.set_session;
|
||||
kp->original_save_session = r->upstream->peer.save_session;
|
||||
r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
|
||||
r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_keepalive_peer_data_t *kp = data;
|
||||
ngx_http_upstream_keepalive_cache_t *item;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_queue_t *q, *cache;
|
||||
ngx_connection_t *c;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get keepalive peer");
|
||||
|
||||
/* ask balancer */
|
||||
|
||||
rc = kp->original_get_peer(pc, kp->data);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* search cache for suitable connection */
|
||||
|
||||
cache = &kp->conf->cache;
|
||||
|
||||
for (q = ngx_queue_head(cache);
|
||||
q != ngx_queue_sentinel(cache);
|
||||
q = ngx_queue_next(q))
|
||||
{
|
||||
item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
|
||||
c = item->connection;
|
||||
|
||||
if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
|
||||
item->socklen, pc->socklen)
|
||||
== 0)
|
||||
{
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&kp->conf->free, q);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get keepalive peer: using connection %p", c);
|
||||
|
||||
c->idle = 0;
|
||||
c->sent = 0;
|
||||
c->log = pc->log;
|
||||
c->read->log = pc->log;
|
||||
c->write->log = pc->log;
|
||||
c->pool->log = pc->log;
|
||||
|
||||
pc->connection = c;
|
||||
pc->cached = 1;
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
|
||||
ngx_uint_t state)
|
||||
{
|
||||
ngx_http_upstream_keepalive_peer_data_t *kp = data;
|
||||
ngx_http_upstream_keepalive_cache_t *item;
|
||||
|
||||
ngx_queue_t *q;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"free keepalive peer");
|
||||
|
||||
/* cache valid connections */
|
||||
|
||||
u = kp->upstream;
|
||||
c = pc->connection;
|
||||
|
||||
if (state & NGX_PEER_FAILED
|
||||
|| c == NULL
|
||||
|| c->read->eof
|
||||
|| c->read->error
|
||||
|| c->read->timedout
|
||||
|| c->write->error
|
||||
|| c->write->timedout)
|
||||
{
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (!u->keepalive) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"free keepalive peer: saving connection %p", c);
|
||||
|
||||
if (ngx_queue_empty(&kp->conf->free)) {
|
||||
|
||||
q = ngx_queue_last(&kp->conf->cache);
|
||||
ngx_queue_remove(q);
|
||||
|
||||
item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
|
||||
|
||||
ngx_http_upstream_keepalive_close(item->connection);
|
||||
|
||||
} else {
|
||||
q = ngx_queue_head(&kp->conf->free);
|
||||
ngx_queue_remove(q);
|
||||
|
||||
item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
|
||||
}
|
||||
|
||||
item->connection = c;
|
||||
ngx_queue_insert_head(&kp->conf->cache, q);
|
||||
|
||||
pc->connection = NULL;
|
||||
|
||||
if (c->read->timer_set) {
|
||||
ngx_del_timer(c->read);
|
||||
}
|
||||
if (c->write->timer_set) {
|
||||
ngx_del_timer(c->write);
|
||||
}
|
||||
|
||||
c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
|
||||
c->read->handler = ngx_http_upstream_keepalive_close_handler;
|
||||
|
||||
c->data = item;
|
||||
c->idle = 1;
|
||||
c->log = ngx_cycle->log;
|
||||
c->read->log = ngx_cycle->log;
|
||||
c->write->log = ngx_cycle->log;
|
||||
c->pool->log = ngx_cycle->log;
|
||||
|
||||
item->socklen = pc->socklen;
|
||||
ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
|
||||
|
||||
if (c->read->ready) {
|
||||
ngx_http_upstream_keepalive_close_handler(c->read);
|
||||
}
|
||||
|
||||
invalid:
|
||||
|
||||
kp->original_free_peer(pc, kp->data, state);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
|
||||
"keepalive dummy handler");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_http_upstream_keepalive_srv_conf_t *conf;
|
||||
ngx_http_upstream_keepalive_cache_t *item;
|
||||
|
||||
int n;
|
||||
char buf[1];
|
||||
ngx_connection_t *c;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
|
||||
"keepalive close handler");
|
||||
|
||||
c = ev->data;
|
||||
|
||||
if (c->close) {
|
||||
goto close;
|
||||
}
|
||||
|
||||
n = recv(c->fd, buf, 1, MSG_PEEK);
|
||||
|
||||
if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
|
||||
ev->ready = 0;
|
||||
|
||||
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
||||
goto close;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
close:
|
||||
|
||||
item = c->data;
|
||||
conf = item->conf;
|
||||
|
||||
ngx_http_upstream_keepalive_close(c);
|
||||
|
||||
ngx_queue_remove(&item->queue);
|
||||
ngx_queue_insert_head(&conf->free, &item->queue);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_keepalive_close(ngx_connection_t *c)
|
||||
{
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
if (c->ssl) {
|
||||
c->ssl->no_wait_shutdown = 1;
|
||||
c->ssl->no_send_shutdown = 1;
|
||||
|
||||
if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
|
||||
c->ssl->handler = ngx_http_upstream_keepalive_close;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ngx_destroy_pool(c->pool);
|
||||
ngx_close_connection(c);
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_keepalive_peer_data_t *kp = data;
|
||||
|
||||
return kp->original_set_session(pc, kp->data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_keepalive_peer_data_t *kp = data;
|
||||
|
||||
kp->original_save_session(pc, kp->data);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_upstream_keepalive_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(ngx_http_upstream_keepalive_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->original_init_upstream = NULL;
|
||||
* conf->original_init_peer = NULL;
|
||||
*/
|
||||
|
||||
conf->max_cached = 1;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_upstream_srv_conf_t *uscf;
|
||||
ngx_http_upstream_keepalive_srv_conf_t *kcf = conf;
|
||||
|
||||
ngx_int_t n;
|
||||
ngx_str_t *value;
|
||||
|
||||
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
|
||||
|
||||
if (kcf->original_init_upstream) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
kcf->original_init_upstream = uscf->peer.init_upstream
|
||||
? uscf->peer.init_upstream
|
||||
: ngx_http_upstream_init_round_robin;
|
||||
|
||||
uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
|
||||
|
||||
/* read options */
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
n = ngx_atoi(value[1].data, value[1].len);
|
||||
|
||||
if (n == NGX_ERROR || n == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid value \"%V\" in \"%V\" directive",
|
||||
&value[1], &cmd->name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
kcf->max_cached = n;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t *conns;
|
||||
} ngx_http_upstream_least_conn_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
ngx_http_upstream_rr_peer_data_t rrp;
|
||||
|
||||
ngx_uint_t *conns;
|
||||
|
||||
ngx_event_get_peer_pt get_rr_peer;
|
||||
ngx_event_free_peer_pt free_rr_peer;
|
||||
} ngx_http_upstream_lc_peer_data_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_get_least_conn_peer(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t state);
|
||||
static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_upstream_least_conn_commands[] = {
|
||||
|
||||
{ ngx_string("least_conn"),
|
||||
NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
|
||||
ngx_http_upstream_least_conn,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_upstream_least_conn_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_http_upstream_least_conn_create_conf, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_upstream_least_conn_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_upstream_least_conn_module_ctx, /* module context */
|
||||
ngx_http_upstream_least_conn_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_int_t
|
||||
ngx_http_upstream_init_least_conn(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
ngx_http_upstream_least_conn_conf_t *lcf;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
|
||||
"init least conn");
|
||||
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers = us->peer.data;
|
||||
|
||||
n = peers->number;
|
||||
|
||||
if (peers->next) {
|
||||
n += peers->next->number;
|
||||
}
|
||||
|
||||
lcf = ngx_http_conf_upstream_srv_conf(us,
|
||||
ngx_http_upstream_least_conn_module);
|
||||
|
||||
lcf->conns = ngx_pcalloc(cf->pool, sizeof(ngx_uint_t) * n);
|
||||
if (lcf->conns == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_least_conn_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_http_upstream_lc_peer_data_t *lcp;
|
||||
ngx_http_upstream_least_conn_conf_t *lcf;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"init least conn peer");
|
||||
|
||||
lcf = ngx_http_conf_upstream_srv_conf(us,
|
||||
ngx_http_upstream_least_conn_module);
|
||||
|
||||
lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));
|
||||
if (lcp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
lcp->conns = lcf->conns;
|
||||
|
||||
r->upstream->peer.data = &lcp->rrp;
|
||||
|
||||
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;
|
||||
r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;
|
||||
|
||||
lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
|
||||
lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_lc_peer_data_t *lcp = data;
|
||||
|
||||
time_t now;
|
||||
uintptr_t m;
|
||||
ngx_int_t rc, total;
|
||||
ngx_uint_t i, n, p, many;
|
||||
ngx_http_upstream_rr_peer_t *peer, *best;
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get least conn peer, try: %ui", pc->tries);
|
||||
|
||||
if (lcp->rrp.peers->single) {
|
||||
return lcp->get_rr_peer(pc, &lcp->rrp);
|
||||
}
|
||||
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
peers = lcp->rrp.peers;
|
||||
|
||||
best = NULL;
|
||||
total = 0;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
many = 0;
|
||||
p = 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < peers->number; i++) {
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (lcp->rrp.tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer = &peers->peer[i];
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* select peer with least number of connections; if there are
|
||||
* multiple peers with the same number of connections, select
|
||||
* based on round-robin
|
||||
*/
|
||||
|
||||
if (best == NULL
|
||||
|| lcp->conns[i] * best->weight < lcp->conns[p] * peer->weight)
|
||||
{
|
||||
best = peer;
|
||||
many = 0;
|
||||
p = i;
|
||||
|
||||
} else if (lcp->conns[i] * best->weight
|
||||
== lcp->conns[p] * peer->weight)
|
||||
{
|
||||
many = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get least conn peer, no peer found");
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (many) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get least conn peer, many");
|
||||
|
||||
for (i = p; i < peers->number; i++) {
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (lcp->rrp.tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer = &peers->peer[i];
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best->current_weight -= total;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
pc->sockaddr = best->sockaddr;
|
||||
pc->socklen = best->socklen;
|
||||
pc->name = &best->name;
|
||||
|
||||
lcp->rrp.current = p;
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
lcp->rrp.tried[n] |= m;
|
||||
lcp->conns[p]++;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
if (peers->next) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get least conn peer, backup servers");
|
||||
|
||||
lcp->conns += peers->number;
|
||||
|
||||
lcp->rrp.peers = peers->next;
|
||||
|
||||
n = (lcp->rrp.peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
lcp->rrp.tried[i] = 0;
|
||||
}
|
||||
|
||||
rc = ngx_http_upstream_get_least_conn_peer(pc, lcp);
|
||||
|
||||
if (rc != NGX_BUSY) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* all peers failed, mark them as live for quick recovery */
|
||||
|
||||
for (i = 0; i < peers->number; i++) {
|
||||
peers->peer[i].fails = 0;
|
||||
}
|
||||
|
||||
pc->name = peers->name;
|
||||
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t state)
|
||||
{
|
||||
ngx_http_upstream_lc_peer_data_t *lcp = data;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"free least conn peer %ui %ui", pc->tries, state);
|
||||
|
||||
if (lcp->rrp.peers->single) {
|
||||
lcp->free_rr_peer(pc, &lcp->rrp, state);
|
||||
return;
|
||||
}
|
||||
|
||||
lcp->conns[lcp->rrp.current]--;
|
||||
|
||||
lcp->free_rr_peer(pc, &lcp->rrp, state);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_upstream_least_conn_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(ngx_http_upstream_least_conn_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->conns = NULL;
|
||||
*/
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_upstream_srv_conf_t *uscf;
|
||||
|
||||
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
|
||||
|
||||
if (uscf->peer.init_upstream) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"load balancing method redefined");
|
||||
}
|
||||
|
||||
uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;
|
||||
|
||||
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|
||||
|NGX_HTTP_UPSTREAM_WEIGHT
|
||||
|NGX_HTTP_UPSTREAM_MAX_FAILS
|
||||
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_HTTP_UPSTREAM_DOWN
|
||||
|NGX_HTTP_UPSTREAM_BACKUP;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_USERID_OFF 0
|
||||
#define NGX_HTTP_USERID_LOG 1
|
||||
#define NGX_HTTP_USERID_V1 2
|
||||
#define NGX_HTTP_USERID_ON 3
|
||||
|
||||
/* 31 Dec 2037 23:55:55 GMT */
|
||||
#define NGX_HTTP_USERID_MAX_EXPIRES 2145916555
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t enable;
|
||||
|
||||
ngx_int_t service;
|
||||
|
||||
ngx_str_t name;
|
||||
ngx_str_t domain;
|
||||
ngx_str_t path;
|
||||
ngx_str_t p3p;
|
||||
|
||||
time_t expires;
|
||||
|
||||
u_char mark;
|
||||
} ngx_http_userid_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t uid_got[4];
|
||||
uint32_t uid_set[4];
|
||||
ngx_str_t cookie;
|
||||
ngx_uint_t reset;
|
||||
} ngx_http_userid_ctx_t;
|
||||
|
||||
|
||||
static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,
|
||||
ngx_http_userid_conf_t *conf);
|
||||
static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);
|
||||
static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
|
||||
ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
|
||||
static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,
|
||||
ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
|
||||
|
||||
static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);
|
||||
static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data);
|
||||
static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data);
|
||||
static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
|
||||
static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);
|
||||
|
||||
|
||||
|
||||
static uint32_t start_value;
|
||||
static uint32_t sequencer_v1 = 1;
|
||||
static uint32_t sequencer_v2 = 0x03030302;
|
||||
|
||||
|
||||
static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT";
|
||||
|
||||
|
||||
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_userid_state[] = {
|
||||
{ ngx_string("off"), NGX_HTTP_USERID_OFF },
|
||||
{ ngx_string("log"), NGX_HTTP_USERID_LOG },
|
||||
{ ngx_string("v1"), NGX_HTTP_USERID_V1 },
|
||||
{ ngx_string("on"), NGX_HTTP_USERID_ON },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_post_handler_pt ngx_http_userid_domain_p =
|
||||
ngx_http_userid_domain;
|
||||
static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path;
|
||||
static ngx_conf_post_handler_pt ngx_http_userid_p3p_p = ngx_http_userid_p3p;
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_userid_commands[] = {
|
||||
|
||||
{ ngx_string("userid"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, enable),
|
||||
ngx_http_userid_state },
|
||||
|
||||
{ ngx_string("userid_service"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, service),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("userid_name"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, name),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("userid_domain"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, domain),
|
||||
&ngx_http_userid_domain_p },
|
||||
|
||||
{ ngx_string("userid_path"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, path),
|
||||
&ngx_http_userid_path_p },
|
||||
|
||||
{ ngx_string("userid_expires"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_userid_expires,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("userid_p3p"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_userid_conf_t, p3p),
|
||||
&ngx_http_userid_p3p_p },
|
||||
|
||||
{ ngx_string("userid_mark"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_userid_mark,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_userid_filter_module_ctx = {
|
||||
ngx_http_userid_add_variables, /* preconfiguration */
|
||||
ngx_http_userid_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_userid_create_conf, /* create location configuration */
|
||||
ngx_http_userid_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_userid_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_userid_filter_module_ctx, /* module context */
|
||||
ngx_http_userid_commands, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
ngx_http_userid_init_worker, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_userid_got = ngx_string("uid_got");
|
||||
static ngx_str_t ngx_http_userid_set = ngx_string("uid_set");
|
||||
static ngx_str_t ngx_http_userid_reset = ngx_string("uid_reset");
|
||||
static ngx_uint_t ngx_http_userid_reset_index;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_filter(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_userid_ctx_t *ctx;
|
||||
ngx_http_userid_conf_t *conf;
|
||||
|
||||
if (r != r->main) {
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
|
||||
|
||||
if (conf->enable < NGX_HTTP_USERID_V1) {
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
ctx = ngx_http_userid_get_uid(r, conf);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_got_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_userid_ctx_t *ctx;
|
||||
ngx_http_userid_conf_t *conf;
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
|
||||
|
||||
if (conf->enable == NGX_HTTP_USERID_OFF) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx = ngx_http_userid_get_uid(r->main, conf);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->uid_got[3] != 0) {
|
||||
return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);
|
||||
}
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_set_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_userid_ctx_t *ctx;
|
||||
ngx_http_userid_conf_t *conf;
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
|
||||
|
||||
if (conf->enable < NGX_HTTP_USERID_V1) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx = ngx_http_userid_get_uid(r->main, conf);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->uid_set[3] == 0) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);
|
||||
}
|
||||
|
||||
|
||||
static ngx_http_userid_ctx_t *
|
||||
ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)
|
||||
{
|
||||
ngx_int_t n;
|
||||
ngx_str_t src, dst;
|
||||
ngx_table_elt_t **cookies;
|
||||
ngx_http_userid_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
|
||||
|
||||
if (ctx) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
|
||||
}
|
||||
|
||||
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
|
||||
&ctx->cookie);
|
||||
if (n == NGX_DECLINED) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"uid cookie: \"%V\"", &ctx->cookie);
|
||||
|
||||
if (ctx->cookie.len < 22) {
|
||||
cookies = r->headers_in.cookies.elts;
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"client sent too short userid cookie \"%V\"",
|
||||
&cookies[n]->value);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
src = ctx->cookie;
|
||||
|
||||
/*
|
||||
* we have to limit the encoded string to 22 characters because
|
||||
* 1) cookie may be marked by "userid_mark",
|
||||
* 2) and there are already the millions cookies with a garbage
|
||||
* instead of the correct base64 trail "=="
|
||||
*/
|
||||
|
||||
src.len = 22;
|
||||
|
||||
dst.data = (u_char *) ctx->uid_got;
|
||||
|
||||
if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {
|
||||
cookies = r->headers_in.cookies.elts;
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"client sent invalid userid cookie \"%V\"",
|
||||
&cookies[n]->value);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"uid: %08XD%08XD%08XD%08XD",
|
||||
ctx->uid_got[0], ctx->uid_got[1],
|
||||
ctx->uid_got[2], ctx->uid_got[3]);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
|
||||
ngx_http_userid_conf_t *conf)
|
||||
{
|
||||
u_char *cookie, *p;
|
||||
size_t len;
|
||||
ngx_str_t src, dst;
|
||||
ngx_table_elt_t *set_cookie, *p3p;
|
||||
|
||||
if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->uid_set[3] == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
|
||||
|
||||
if (conf->expires) {
|
||||
len += sizeof(expires) - 1 + 2;
|
||||
}
|
||||
|
||||
if (conf->domain.len) {
|
||||
len += conf->domain.len;
|
||||
}
|
||||
|
||||
cookie = ngx_pnalloc(r->pool, len);
|
||||
if (cookie == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_copy(cookie, conf->name.data, conf->name.len);
|
||||
*p++ = '=';
|
||||
|
||||
if (ctx->uid_got[3] == 0 || ctx->reset) {
|
||||
src.len = 16;
|
||||
src.data = (u_char *) ctx->uid_set;
|
||||
dst.data = p;
|
||||
|
||||
ngx_encode_base64(&dst, &src);
|
||||
|
||||
p += dst.len;
|
||||
|
||||
if (conf->mark) {
|
||||
*(p - 2) = conf->mark;
|
||||
}
|
||||
|
||||
} else {
|
||||
p = ngx_cpymem(p, ctx->cookie.data, 22);
|
||||
*p++ = conf->mark;
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
|
||||
p = ngx_cpymem(p, expires, sizeof(expires) - 1);
|
||||
|
||||
} else if (conf->expires) {
|
||||
p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
|
||||
p = ngx_http_cookie_time(p, ngx_time() + conf->expires);
|
||||
}
|
||||
|
||||
p = ngx_copy(p, conf->domain.data, conf->domain.len);
|
||||
|
||||
p = ngx_copy(p, conf->path.data, conf->path.len);
|
||||
|
||||
set_cookie = ngx_list_push(&r->headers_out.headers);
|
||||
if (set_cookie == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
set_cookie->hash = 1;
|
||||
ngx_str_set(&set_cookie->key, "Set-Cookie");
|
||||
set_cookie->value.len = p - cookie;
|
||||
set_cookie->value.data = cookie;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"uid cookie: \"%V\"", &set_cookie->value);
|
||||
|
||||
if (conf->p3p.len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
p3p = ngx_list_push(&r->headers_out.headers);
|
||||
if (p3p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p3p->hash = 1;
|
||||
ngx_str_set(&p3p->key, "P3P");
|
||||
p3p->value = conf->p3p;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
|
||||
ngx_http_userid_conf_t *conf)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
struct sockaddr_in *sin;
|
||||
ngx_http_variable_value_t *vv;
|
||||
#if (NGX_HAVE_INET6)
|
||||
u_char *p;
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
|
||||
if (ctx->uid_set[3] != 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->uid_got[3] != 0) {
|
||||
|
||||
vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);
|
||||
|
||||
if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {
|
||||
|
||||
if (conf->mark == '\0'
|
||||
|| (ctx->cookie.len > 23
|
||||
&& ctx->cookie.data[22] == conf->mark
|
||||
&& ctx->cookie.data[23] == '='))
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->uid_set[0] = ctx->uid_got[0];
|
||||
ctx->uid_set[1] = ctx->uid_got[1];
|
||||
ctx->uid_set[2] = ctx->uid_got[2];
|
||||
ctx->uid_set[3] = ctx->uid_got[3];
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
} else {
|
||||
ctx->reset = 1;
|
||||
|
||||
if (vv->len == 3 && ngx_strncmp(vv->data, "log", 3) == 0) {
|
||||
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
||||
"userid cookie \"%V=%08XD%08XD%08XD%08XD\" was reset",
|
||||
&conf->name, ctx->uid_got[0], ctx->uid_got[1],
|
||||
ctx->uid_got[2], ctx->uid_got[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: in the threaded mode the sequencers should be in TLS and their
|
||||
* ranges should be divided between threads
|
||||
*/
|
||||
|
||||
if (conf->enable == NGX_HTTP_USERID_V1) {
|
||||
if (conf->service == NGX_CONF_UNSET) {
|
||||
ctx->uid_set[0] = 0;
|
||||
} else {
|
||||
ctx->uid_set[0] = conf->service;
|
||||
}
|
||||
ctx->uid_set[1] = (uint32_t) ngx_time();
|
||||
ctx->uid_set[2] = start_value;
|
||||
ctx->uid_set[3] = sequencer_v1;
|
||||
sequencer_v1 += 0x100;
|
||||
|
||||
} else {
|
||||
if (conf->service == NGX_CONF_UNSET) {
|
||||
|
||||
c = r->connection;
|
||||
|
||||
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
switch (c->local_sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
|
||||
|
||||
p = (u_char *) &ctx->uid_set[0];
|
||||
|
||||
*p++ = sin6->sin6_addr.s6_addr[12];
|
||||
*p++ = sin6->sin6_addr.s6_addr[13];
|
||||
*p++ = sin6->sin6_addr.s6_addr[14];
|
||||
*p = sin6->sin6_addr.s6_addr[15];
|
||||
|
||||
break;
|
||||
#endif
|
||||
default: /* AF_INET */
|
||||
sin = (struct sockaddr_in *) c->local_sockaddr;
|
||||
ctx->uid_set[0] = sin->sin_addr.s_addr;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
ctx->uid_set[0] = htonl(conf->service);
|
||||
}
|
||||
|
||||
ctx->uid_set[1] = htonl((uint32_t) ngx_time());
|
||||
ctx->uid_set[2] = htonl(start_value);
|
||||
ctx->uid_set[3] = htonl(sequencer_v2);
|
||||
sequencer_v2 += 0x100;
|
||||
if (sequencer_v2 < 0x03030302) {
|
||||
sequencer_v2 = 0x03030302;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
||||
ngx_str_t *name, uint32_t *uid)
|
||||
{
|
||||
v->len = name->len + sizeof("=00001111222233334444555566667777") - 1;
|
||||
v->data = ngx_pnalloc(r->pool, v->len);
|
||||
if (v->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",
|
||||
name, uid[0], uid[1], uid[2], uid[3]);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_reset_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
*v = ngx_http_variable_null_value;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_int_t n;
|
||||
ngx_http_variable_t *var;
|
||||
|
||||
var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_userid_got_variable;
|
||||
|
||||
var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_userid_set_variable;
|
||||
|
||||
var = ngx_http_add_variable(cf, &ngx_http_userid_reset,
|
||||
NGX_HTTP_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_http_userid_reset_variable;
|
||||
|
||||
n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);
|
||||
if (n == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_userid_reset_index = n;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_userid_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_userid_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->name = { 0, NULL };
|
||||
* conf->domain = { 0, NULL };
|
||||
* conf->path = { 0, NULL };
|
||||
* conf->p3p = { 0, NULL };
|
||||
*/
|
||||
|
||||
conf->enable = NGX_CONF_UNSET_UINT;
|
||||
conf->service = NGX_CONF_UNSET;
|
||||
conf->expires = NGX_CONF_UNSET;
|
||||
conf->mark = (u_char) '\xFF';
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_userid_conf_t *prev = parent;
|
||||
ngx_http_userid_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_uint_value(conf->enable, prev->enable,
|
||||
NGX_HTTP_USERID_OFF);
|
||||
|
||||
ngx_conf_merge_str_value(conf->name, prev->name, "uid");
|
||||
ngx_conf_merge_str_value(conf->domain, prev->domain, "");
|
||||
ngx_conf_merge_str_value(conf->path, prev->path, "; path=/");
|
||||
ngx_conf_merge_str_value(conf->p3p, prev->p3p, "");
|
||||
|
||||
ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);
|
||||
ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
|
||||
|
||||
if (conf->mark == (u_char) '\xFF') {
|
||||
if (prev->mark == (u_char) '\xFF') {
|
||||
conf->mark = '\0';
|
||||
} else {
|
||||
conf->mark = prev->mark;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_userid_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
ngx_str_t *domain = data;
|
||||
|
||||
u_char *p, *new;
|
||||
|
||||
if (ngx_strcmp(domain->data, "none") == 0) {
|
||||
ngx_str_set(domain, "");
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len);
|
||||
if (new == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(new, "; domain=", sizeof("; domain=") - 1);
|
||||
ngx_memcpy(p, domain->data, domain->len);
|
||||
|
||||
domain->len += sizeof("; domain=") - 1;
|
||||
domain->data = new;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
ngx_str_t *path = data;
|
||||
|
||||
u_char *p, *new;
|
||||
|
||||
new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len);
|
||||
if (new == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(new, "; path=", sizeof("; path=") - 1);
|
||||
ngx_memcpy(p, path->data, path->len);
|
||||
|
||||
path->len += sizeof("; path=") - 1;
|
||||
path->data = new;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_userid_conf_t *ucf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (ucf->expires != NGX_CONF_UNSET) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "max") == 0) {
|
||||
ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[1].data, "off") == 0) {
|
||||
ucf->expires = 0;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
ucf->expires = ngx_parse_time(&value[1], 1);
|
||||
if (ucf->expires == (time_t) NGX_ERROR) {
|
||||
return "invalid value";
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
ngx_str_t *p3p = data;
|
||||
|
||||
if (ngx_strcmp(p3p->data, "none") == 0) {
|
||||
ngx_str_set(p3p, "");
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_userid_conf_t *ucf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (ucf->mark != (u_char) '\xFF') {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "off") == 0) {
|
||||
ucf->mark = '\0';
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (value[1].len != 1
|
||||
|| !((value[1].data[0] >= '0' && value[1].data[0] <= '9')
|
||||
|| (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')
|
||||
|| (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')
|
||||
|| value[1].data[0] == '='))
|
||||
{
|
||||
return "value must be \"off\" or a single letter, digit or \"=\"";
|
||||
}
|
||||
|
||||
ucf->mark = value[1].data[0];
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_userid_init_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
struct timeval tp;
|
||||
|
||||
ngx_gettimeofday(&tp);
|
||||
|
||||
/* use the most significant usec part that fits to 16 bits */
|
||||
start_value = ((tp.tv_usec / 20) << 16) | ngx_pid;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
|
||||
# Copyright (C) Igor Sysoev
|
||||
# Copyright (C) Nginx, Inc.
|
||||
|
||||
use 5.006001;
|
||||
use ExtUtils::MakeMaker;
|
||||
|
||||
WriteMakefile(
|
||||
NAME => 'nginx',
|
||||
VERSION_FROM => 'nginx.pm', # finds $VERSION
|
||||
PREREQ_PM => {}, # e.g., Module::Name => 1.1
|
||||
|
||||
ABSTRACT_FROM => 'nginx.pm', # retrieve abstract from module
|
||||
AUTHOR => 'Igor Sysoev',
|
||||
|
||||
CCFLAGS => "$ENV{NGX_PM_CFLAGS}",
|
||||
OPTIMIZE => '-O',
|
||||
|
||||
INC => join(" ", map {
|
||||
m#^/# ? "-I $_" : "-I ../../../../../$_"
|
||||
} (split /\s+/, $ENV{NGX_INCS})),
|
||||
|
||||
depend => {
|
||||
'nginx.c' => join(" ", map {
|
||||
m#^/# ? $_ : "../../../../../$_"
|
||||
} (split(/\s+/, $ENV{NGX_DEPS}),
|
||||
"src/http/modules/perl/ngx_http_perl_module.h"))
|
||||
},
|
||||
|
||||
PM => {
|
||||
'nginx.pm' => '$(INST_LIBDIR)/nginx.pm'
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,138 @@
|
||||
package nginx;
|
||||
|
||||
use 5.006001;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our @EXPORT = qw(
|
||||
OK
|
||||
DECLINED
|
||||
|
||||
HTTP_OK
|
||||
HTTP_CREATED
|
||||
HTTP_ACCEPTED
|
||||
HTTP_NO_CONTENT
|
||||
HTTP_PARTIAL_CONTENT
|
||||
|
||||
HTTP_MOVED_PERMANENTLY
|
||||
HTTP_MOVED_TEMPORARILY
|
||||
HTTP_REDIRECT
|
||||
HTTP_SEE_OTHER
|
||||
HTTP_NOT_MODIFIED
|
||||
HTTP_TEMPORARY_REDIRECT
|
||||
|
||||
HTTP_BAD_REQUEST
|
||||
HTTP_UNAUTHORIZED
|
||||
HTTP_PAYMENT_REQUIRED
|
||||
HTTP_FORBIDDEN
|
||||
HTTP_NOT_FOUND
|
||||
HTTP_NOT_ALLOWED
|
||||
HTTP_NOT_ACCEPTABLE
|
||||
HTTP_REQUEST_TIME_OUT
|
||||
HTTP_CONFLICT
|
||||
HTTP_GONE
|
||||
HTTP_LENGTH_REQUIRED
|
||||
HTTP_REQUEST_ENTITY_TOO_LARGE
|
||||
HTTP_REQUEST_URI_TOO_LARGE
|
||||
HTTP_UNSUPPORTED_MEDIA_TYPE
|
||||
HTTP_RANGE_NOT_SATISFIABLE
|
||||
|
||||
HTTP_INTERNAL_SERVER_ERROR
|
||||
HTTP_SERVER_ERROR
|
||||
HTTP_NOT_IMPLEMENTED
|
||||
HTTP_BAD_GATEWAY
|
||||
HTTP_SERVICE_UNAVAILABLE
|
||||
HTTP_GATEWAY_TIME_OUT
|
||||
HTTP_INSUFFICIENT_STORAGE
|
||||
);
|
||||
|
||||
our $VERSION = '%%VERSION%%';
|
||||
|
||||
require XSLoader;
|
||||
XSLoader::load('nginx', $VERSION);
|
||||
|
||||
# Preloaded methods go here.
|
||||
|
||||
use constant OK => 0;
|
||||
use constant DECLINED => -5;
|
||||
|
||||
use constant HTTP_OK => 200;
|
||||
use constant HTTP_CREATED => 201;
|
||||
use constant HTTP_ACCEPTED => 202;
|
||||
use constant HTTP_NO_CONTENT => 204;
|
||||
use constant HTTP_PARTIAL_CONTENT => 206;
|
||||
|
||||
use constant HTTP_MOVED_PERMANENTLY => 301;
|
||||
use constant HTTP_MOVED_TEMPORARILY => 302;
|
||||
use constant HTTP_REDIRECT => 302;
|
||||
use constant HTTP_SEE_OTHER => 303;
|
||||
use constant HTTP_NOT_MODIFIED => 304;
|
||||
use constant HTTP_TEMPORARY_REDIRECT => 307;
|
||||
|
||||
use constant HTTP_BAD_REQUEST => 400;
|
||||
use constant HTTP_UNAUTHORIZED => 401;
|
||||
use constant HTTP_PAYMENT_REQUIRED => 402;
|
||||
use constant HTTP_FORBIDDEN => 403;
|
||||
use constant HTTP_NOT_FOUND => 404;
|
||||
use constant HTTP_NOT_ALLOWED => 405;
|
||||
use constant HTTP_NOT_ACCEPTABLE => 406;
|
||||
use constant HTTP_REQUEST_TIME_OUT => 408;
|
||||
use constant HTTP_CONFLICT => 409;
|
||||
use constant HTTP_GONE => 410;
|
||||
use constant HTTP_LENGTH_REQUIRED => 411;
|
||||
use constant HTTP_REQUEST_ENTITY_TOO_LARGE => 413;
|
||||
use constant HTTP_REQUEST_URI_TOO_LARGE => 414;
|
||||
use constant HTTP_UNSUPPORTED_MEDIA_TYPE => 415;
|
||||
use constant HTTP_RANGE_NOT_SATISFIABLE => 416;
|
||||
|
||||
use constant HTTP_INTERNAL_SERVER_ERROR => 500;
|
||||
use constant HTTP_SERVER_ERROR => 500;
|
||||
use constant HTTP_NOT_IMPLEMENTED => 501;
|
||||
use constant HTTP_BAD_GATEWAY => 502;
|
||||
use constant HTTP_SERVICE_UNAVAILABLE => 503;
|
||||
use constant HTTP_GATEWAY_TIME_OUT => 504;
|
||||
use constant HTTP_INSUFFICIENT_STORAGE => 507;
|
||||
|
||||
|
||||
sub rflush {
|
||||
my $r = shift;
|
||||
|
||||
$r->flush;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
nginx - Perl interface to the nginx HTTP server API
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use nginx;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides a Perl interface to the nginx HTTP server API.
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
http://nginx.org/en/docs/http/ngx_http_perl_module.html
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Igor Sysoev
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) Igor Sysoev
|
||||
Copyright (C) Nginx, Inc.
|
||||
|
||||
|
||||
=cut
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_PERL_MODULE_H_INCLUDED_
|
||||
#define _NGX_HTTP_PERL_MODULE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <nginx.h>
|
||||
|
||||
#include <EXTERN.h>
|
||||
#include <perl.h>
|
||||
|
||||
|
||||
typedef ngx_http_request_t *nginx;
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t filename;
|
||||
ngx_str_t redirect_uri;
|
||||
ngx_str_t redirect_args;
|
||||
|
||||
SV *next;
|
||||
|
||||
ngx_uint_t done; /* unsigned done:1; */
|
||||
|
||||
ngx_array_t *variables; /* array of ngx_http_perl_var_t */
|
||||
|
||||
#if (NGX_HTTP_SSI)
|
||||
ngx_http_ssi_ctx_t *ssi;
|
||||
#endif
|
||||
} ngx_http_perl_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t hash;
|
||||
ngx_str_t name;
|
||||
ngx_str_t value;
|
||||
} ngx_http_perl_var_t;
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_perl_module;
|
||||
|
||||
|
||||
/*
|
||||
* workaround for "unused variable `Perl___notused'" warning
|
||||
* when building with perl 5.6.1
|
||||
*/
|
||||
#ifndef PERL_IMPLICIT_CONTEXT
|
||||
#undef dTHXa
|
||||
#define dTHXa(a)
|
||||
#endif
|
||||
|
||||
|
||||
extern void boot_DynaLoader(pTHX_ CV* cv);
|
||||
|
||||
|
||||
void ngx_http_perl_handle_request(ngx_http_request_t *r);
|
||||
void ngx_http_perl_sleep_handler(ngx_http_request_t *r);
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */
|
||||
@@ -0,0 +1,3 @@
|
||||
TYPEMAP
|
||||
|
||||
nginx T_PTROBJ
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_H_INCLUDED_
|
||||
#define _NGX_HTTP_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
typedef struct ngx_http_request_s ngx_http_request_t;
|
||||
typedef struct ngx_http_upstream_s ngx_http_upstream_t;
|
||||
typedef struct ngx_http_cache_s ngx_http_cache_t;
|
||||
typedef struct ngx_http_file_cache_s ngx_http_file_cache_t;
|
||||
typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t;
|
||||
typedef struct ngx_http_chunked_s ngx_http_chunked_t;
|
||||
|
||||
#if (NGX_HTTP_SPDY)
|
||||
typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t;
|
||||
#endif
|
||||
|
||||
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h, ngx_uint_t offset);
|
||||
typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,
|
||||
ngx_http_request_t *sr, u_char *buf, size_t len);
|
||||
|
||||
|
||||
#include <ngx_http_variables.h>
|
||||
#include <ngx_http_config.h>
|
||||
#include <ngx_http_request.h>
|
||||
#include <ngx_http_script.h>
|
||||
#include <ngx_http_upstream.h>
|
||||
#include <ngx_http_upstream_round_robin.h>
|
||||
#include <ngx_http_core_module.h>
|
||||
|
||||
#if (NGX_HTTP_SPDY)
|
||||
#include <ngx_http_spdy.h>
|
||||
#endif
|
||||
#if (NGX_HTTP_CACHE)
|
||||
#include <ngx_http_cache.h>
|
||||
#endif
|
||||
#if (NGX_HTTP_SSI)
|
||||
#include <ngx_http_ssi_filter_module.h>
|
||||
#endif
|
||||
#if (NGX_HTTP_SSL)
|
||||
#include <ngx_http_ssl_module.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct ngx_http_log_ctx_s {
|
||||
ngx_connection_t *connection;
|
||||
ngx_http_request_t *request;
|
||||
ngx_http_request_t *current_request;
|
||||
};
|
||||
|
||||
|
||||
struct ngx_http_chunked_s {
|
||||
ngx_uint_t state;
|
||||
off_t size;
|
||||
off_t length;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t http_version;
|
||||
ngx_uint_t code;
|
||||
ngx_uint_t count;
|
||||
u_char *start;
|
||||
u_char *end;
|
||||
} ngx_http_status_t;
|
||||
|
||||
|
||||
#define ngx_http_get_module_ctx(r, module) (r)->ctx[module.ctx_index]
|
||||
#define ngx_http_set_ctx(r, c, module) r->ctx[module.ctx_index] = c;
|
||||
|
||||
|
||||
ngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
|
||||
ngx_http_core_loc_conf_t *clcf);
|
||||
ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
|
||||
ngx_http_listen_opt_t *lsopt);
|
||||
|
||||
|
||||
void ngx_http_init_connection(ngx_connection_t *c);
|
||||
void ngx_http_close_connection(ngx_connection_t *c);
|
||||
|
||||
#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
|
||||
int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
|
||||
#endif
|
||||
|
||||
ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);
|
||||
ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,
|
||||
ngx_uint_t merge_slashes);
|
||||
ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
ngx_http_status_t *status);
|
||||
ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
|
||||
ngx_str_t *args, ngx_uint_t *flags);
|
||||
ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
ngx_uint_t allow_underscores);
|
||||
ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,
|
||||
ngx_str_t *name, ngx_str_t *value);
|
||||
ngx_int_t ngx_http_parse_set_cookie_lines(ngx_array_t *headers,
|
||||
ngx_str_t *name, ngx_str_t *value);
|
||||
ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
|
||||
ngx_str_t *value);
|
||||
void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,
|
||||
ngx_str_t *args);
|
||||
ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
|
||||
ngx_http_chunked_t *ctx);
|
||||
|
||||
|
||||
ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);
|
||||
ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
|
||||
void ngx_http_process_request(ngx_http_request_t *r);
|
||||
void ngx_http_update_location_config(ngx_http_request_t *r);
|
||||
void ngx_http_handler(ngx_http_request_t *r);
|
||||
void ngx_http_run_posted_requests(ngx_connection_t *c);
|
||||
ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
|
||||
ngx_http_posted_request_t *pr);
|
||||
void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
|
||||
void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);
|
||||
|
||||
void ngx_http_empty_handler(ngx_event_t *wev);
|
||||
void ngx_http_request_empty_handler(ngx_http_request_t *r);
|
||||
|
||||
|
||||
#define NGX_HTTP_LAST 1
|
||||
#define NGX_HTTP_FLUSH 2
|
||||
|
||||
ngx_int_t ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags);
|
||||
|
||||
|
||||
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
|
||||
ngx_http_client_body_handler_pt post_handler);
|
||||
ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
|
||||
|
||||
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
|
||||
ngx_int_t error);
|
||||
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
|
||||
ngx_module_t *m, ngx_int_t error);
|
||||
void ngx_http_clean_header(ngx_http_request_t *r);
|
||||
|
||||
|
||||
time_t ngx_http_parse_time(u_char *value, size_t len);
|
||||
size_t ngx_http_get_time(char *buf, time_t t);
|
||||
|
||||
|
||||
|
||||
ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
|
||||
void ngx_http_discarded_request_body_handler(ngx_http_request_t *r);
|
||||
void ngx_http_block_reading(ngx_http_request_t *r);
|
||||
void ngx_http_test_reading(ngx_http_request_t *r);
|
||||
|
||||
|
||||
char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys,
|
||||
ngx_hash_t *types_hash, ngx_array_t **prev_keys,
|
||||
ngx_hash_t *prev_types_hash, ngx_str_t *default_types);
|
||||
ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
|
||||
ngx_str_t *default_type);
|
||||
|
||||
#if (NGX_HTTP_DEGRADATION)
|
||||
ngx_uint_t ngx_http_degraded(ngx_http_request_t *);
|
||||
#endif
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_module;
|
||||
|
||||
extern ngx_str_t ngx_http_html_default_types[];
|
||||
|
||||
|
||||
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
|
||||
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
|
||||
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_H_INCLUDED_ */
|
||||
@@ -0,0 +1,186 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_CACHE_H_INCLUDED_
|
||||
#define _NGX_HTTP_CACHE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_CACHE_MISS 1
|
||||
#define NGX_HTTP_CACHE_BYPASS 2
|
||||
#define NGX_HTTP_CACHE_EXPIRED 3
|
||||
#define NGX_HTTP_CACHE_STALE 4
|
||||
#define NGX_HTTP_CACHE_UPDATING 5
|
||||
#define NGX_HTTP_CACHE_REVALIDATED 6
|
||||
#define NGX_HTTP_CACHE_HIT 7
|
||||
#define NGX_HTTP_CACHE_SCARCE 8
|
||||
|
||||
#define NGX_HTTP_CACHE_KEY_LEN 16
|
||||
#define NGX_HTTP_CACHE_ETAG_LEN 42
|
||||
#define NGX_HTTP_CACHE_VARY_LEN 42
|
||||
|
||||
#define NGX_HTTP_CACHE_VERSION 3
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t status;
|
||||
time_t valid;
|
||||
} ngx_http_cache_valid_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_node_t node;
|
||||
ngx_queue_t queue;
|
||||
|
||||
u_char key[NGX_HTTP_CACHE_KEY_LEN
|
||||
- sizeof(ngx_rbtree_key_t)];
|
||||
|
||||
unsigned count:20;
|
||||
unsigned uses:10;
|
||||
unsigned valid_msec:10;
|
||||
unsigned error:10;
|
||||
unsigned exists:1;
|
||||
unsigned updating:1;
|
||||
unsigned deleting:1;
|
||||
/* 11 unused bits */
|
||||
|
||||
ngx_file_uniq_t uniq;
|
||||
time_t expire;
|
||||
time_t valid_sec;
|
||||
size_t body_start;
|
||||
off_t fs_size;
|
||||
ngx_msec_t lock_time;
|
||||
} ngx_http_file_cache_node_t;
|
||||
|
||||
|
||||
struct ngx_http_cache_s {
|
||||
ngx_file_t file;
|
||||
ngx_array_t keys;
|
||||
uint32_t crc32;
|
||||
u_char key[NGX_HTTP_CACHE_KEY_LEN];
|
||||
u_char main[NGX_HTTP_CACHE_KEY_LEN];
|
||||
|
||||
ngx_file_uniq_t uniq;
|
||||
time_t valid_sec;
|
||||
time_t last_modified;
|
||||
time_t date;
|
||||
|
||||
ngx_str_t etag;
|
||||
ngx_str_t vary;
|
||||
u_char variant[NGX_HTTP_CACHE_KEY_LEN];
|
||||
|
||||
size_t header_start;
|
||||
size_t body_start;
|
||||
off_t length;
|
||||
off_t fs_size;
|
||||
|
||||
ngx_uint_t min_uses;
|
||||
ngx_uint_t error;
|
||||
ngx_uint_t valid_msec;
|
||||
|
||||
ngx_buf_t *buf;
|
||||
|
||||
ngx_http_file_cache_t *file_cache;
|
||||
ngx_http_file_cache_node_t *node;
|
||||
|
||||
#if (NGX_THREADS)
|
||||
ngx_thread_task_t *thread_task;
|
||||
#endif
|
||||
|
||||
ngx_msec_t lock_timeout;
|
||||
ngx_msec_t lock_age;
|
||||
ngx_msec_t lock_time;
|
||||
ngx_msec_t wait_time;
|
||||
|
||||
ngx_event_t wait_event;
|
||||
|
||||
unsigned lock:1;
|
||||
unsigned waiting:1;
|
||||
|
||||
unsigned updated:1;
|
||||
unsigned updating:1;
|
||||
unsigned exists:1;
|
||||
unsigned temp_file:1;
|
||||
unsigned reading:1;
|
||||
unsigned secondary:1;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t version;
|
||||
time_t valid_sec;
|
||||
time_t last_modified;
|
||||
time_t date;
|
||||
uint32_t crc32;
|
||||
u_short valid_msec;
|
||||
u_short header_start;
|
||||
u_short body_start;
|
||||
u_char etag_len;
|
||||
u_char etag[NGX_HTTP_CACHE_ETAG_LEN];
|
||||
u_char vary_len;
|
||||
u_char vary[NGX_HTTP_CACHE_VARY_LEN];
|
||||
u_char variant[NGX_HTTP_CACHE_KEY_LEN];
|
||||
} ngx_http_file_cache_header_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_t rbtree;
|
||||
ngx_rbtree_node_t sentinel;
|
||||
ngx_queue_t queue;
|
||||
ngx_atomic_t cold;
|
||||
ngx_atomic_t loading;
|
||||
off_t size;
|
||||
} ngx_http_file_cache_sh_t;
|
||||
|
||||
|
||||
struct ngx_http_file_cache_s {
|
||||
ngx_http_file_cache_sh_t *sh;
|
||||
ngx_slab_pool_t *shpool;
|
||||
|
||||
ngx_path_t *path;
|
||||
ngx_path_t *temp_path;
|
||||
|
||||
off_t max_size;
|
||||
size_t bsize;
|
||||
|
||||
time_t inactive;
|
||||
|
||||
ngx_uint_t files;
|
||||
ngx_uint_t loader_files;
|
||||
ngx_msec_t last;
|
||||
ngx_msec_t loader_sleep;
|
||||
ngx_msec_t loader_threshold;
|
||||
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
};
|
||||
|
||||
|
||||
ngx_int_t ngx_http_file_cache_new(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_file_cache_create(ngx_http_request_t *r);
|
||||
void ngx_http_file_cache_create_key(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf);
|
||||
void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf);
|
||||
void ngx_http_file_cache_update_header(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_cache_send(ngx_http_request_t *);
|
||||
void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf);
|
||||
time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);
|
||||
|
||||
char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
extern ngx_str_t ngx_http_cache_status[];
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_
|
||||
#define _NGX_HTTP_CONFIG_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
void **main_conf;
|
||||
void **srv_conf;
|
||||
void **loc_conf;
|
||||
} ngx_http_conf_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
|
||||
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
|
||||
|
||||
void *(*create_main_conf)(ngx_conf_t *cf);
|
||||
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
|
||||
|
||||
void *(*create_srv_conf)(ngx_conf_t *cf);
|
||||
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
|
||||
|
||||
void *(*create_loc_conf)(ngx_conf_t *cf);
|
||||
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
|
||||
} ngx_http_module_t;
|
||||
|
||||
|
||||
#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */
|
||||
|
||||
#define NGX_HTTP_MAIN_CONF 0x02000000
|
||||
#define NGX_HTTP_SRV_CONF 0x04000000
|
||||
#define NGX_HTTP_LOC_CONF 0x08000000
|
||||
#define NGX_HTTP_UPS_CONF 0x10000000
|
||||
#define NGX_HTTP_SIF_CONF 0x20000000
|
||||
#define NGX_HTTP_LIF_CONF 0x40000000
|
||||
#define NGX_HTTP_LMT_CONF 0x80000000
|
||||
|
||||
|
||||
#define NGX_HTTP_MAIN_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, main_conf)
|
||||
#define NGX_HTTP_SRV_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, srv_conf)
|
||||
#define NGX_HTTP_LOC_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, loc_conf)
|
||||
|
||||
|
||||
#define ngx_http_get_module_main_conf(r, module) \
|
||||
(r)->main_conf[module.ctx_index]
|
||||
#define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index]
|
||||
#define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]
|
||||
|
||||
|
||||
#define ngx_http_conf_get_module_main_conf(cf, module) \
|
||||
((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
|
||||
#define ngx_http_conf_get_module_srv_conf(cf, module) \
|
||||
((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
|
||||
#define ngx_http_conf_get_module_loc_conf(cf, module) \
|
||||
((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
|
||||
|
||||
#define ngx_http_cycle_get_module_main_conf(cycle, module) \
|
||||
(cycle->conf_ctx[ngx_http_module.index] ? \
|
||||
((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \
|
||||
->main_conf[module.ctx_index]: \
|
||||
NULL)
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */
|
||||
@@ -0,0 +1,344 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_bufs_t bufs;
|
||||
} ngx_http_copy_filter_conf_t;
|
||||
|
||||
|
||||
#if (NGX_HAVE_FILE_AIO)
|
||||
static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,
|
||||
ngx_file_t *file);
|
||||
static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);
|
||||
#if (NGX_HAVE_AIO_SENDFILE)
|
||||
static ssize_t ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file);
|
||||
static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);
|
||||
#endif
|
||||
#endif
|
||||
#if (NGX_THREADS)
|
||||
static ngx_int_t ngx_http_copy_thread_handler(ngx_thread_task_t *task,
|
||||
ngx_file_t *file);
|
||||
static void ngx_http_copy_thread_event_handler(ngx_event_t *ev);
|
||||
#endif
|
||||
|
||||
static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_copy_filter_commands[] = {
|
||||
|
||||
{ ngx_string("output_buffers"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
|
||||
ngx_conf_set_bufs_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_copy_filter_conf_t, bufs),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_copy_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_copy_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_copy_filter_create_conf, /* create location configuration */
|
||||
ngx_http_copy_filter_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_copy_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_copy_filter_module_ctx, /* module context */
|
||||
ngx_http_copy_filter_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_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_connection_t *c;
|
||||
ngx_output_chain_ctx_t *ctx;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_copy_filter_conf_t *conf;
|
||||
|
||||
c = r->connection;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http copy filter: \"%V?%V\"", &r->uri, &r->args);
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
|
||||
|
||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
ctx->sendfile = c->sendfile;
|
||||
ctx->need_in_memory = r->main_filter_need_in_memory
|
||||
|| r->filter_need_in_memory;
|
||||
ctx->need_in_temp = r->filter_need_temporary;
|
||||
|
||||
ctx->alignment = clcf->directio_alignment;
|
||||
|
||||
ctx->pool = r->pool;
|
||||
ctx->bufs = conf->bufs;
|
||||
ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
|
||||
|
||||
ctx->output_filter = (ngx_output_chain_filter_pt)
|
||||
ngx_http_next_body_filter;
|
||||
ctx->filter_ctx = r;
|
||||
|
||||
#if (NGX_HAVE_FILE_AIO)
|
||||
if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
|
||||
ctx->aio_handler = ngx_http_copy_aio_handler;
|
||||
#if (NGX_HAVE_AIO_SENDFILE)
|
||||
ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_THREADS)
|
||||
if (clcf->aio == NGX_HTTP_AIO_THREADS) {
|
||||
ctx->thread_handler = ngx_http_copy_thread_handler;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (in && in->buf && ngx_buf_size(in->buf)) {
|
||||
r->request_output = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
|
||||
ctx->aio = r->aio;
|
||||
#endif
|
||||
|
||||
rc = ngx_output_chain(ctx, in);
|
||||
|
||||
if (ctx->in == NULL) {
|
||||
r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
|
||||
|
||||
} else {
|
||||
r->buffered |= NGX_HTTP_COPY_BUFFERED;
|
||||
}
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_FILE_AIO)
|
||||
|
||||
static void
|
||||
ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
|
||||
{
|
||||
ngx_http_request_t *r;
|
||||
|
||||
r = ctx->filter_ctx;
|
||||
|
||||
file->aio->data = r;
|
||||
file->aio->handler = ngx_http_copy_aio_event_handler;
|
||||
|
||||
r->main->blocked++;
|
||||
r->aio = 1;
|
||||
ctx->aio = 1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_copy_aio_event_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_event_aio_t *aio;
|
||||
ngx_http_request_t *r;
|
||||
|
||||
aio = ev->data;
|
||||
r = aio->data;
|
||||
|
||||
r->main->blocked--;
|
||||
r->aio = 0;
|
||||
|
||||
r->connection->write->handler(r->connection->write);
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_AIO_SENDFILE)
|
||||
|
||||
static ssize_t
|
||||
ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file)
|
||||
{
|
||||
ssize_t n;
|
||||
static u_char buf[1];
|
||||
ngx_event_aio_t *aio;
|
||||
ngx_http_request_t *r;
|
||||
|
||||
n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL);
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
aio = file->file->aio;
|
||||
aio->handler = ngx_http_copy_aio_sendfile_event_handler;
|
||||
|
||||
r = aio->data;
|
||||
r->main->blocked++;
|
||||
r->aio = 1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_event_aio_t *aio;
|
||||
ngx_http_request_t *r;
|
||||
|
||||
aio = ev->data;
|
||||
r = aio->data;
|
||||
|
||||
r->main->blocked--;
|
||||
r->aio = 0;
|
||||
ev->complete = 0;
|
||||
|
||||
r->connection->write->handler(r->connection->write);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if (NGX_THREADS)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
|
||||
{
|
||||
ngx_str_t name;
|
||||
ngx_thread_pool_t *tp;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
r = file->thread_ctx;
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
tp = clcf->thread_pool;
|
||||
|
||||
if (tp == NULL) {
|
||||
if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
|
||||
|
||||
if (tp == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"thread pool \"%V\" not found", &name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
task->event.data = r;
|
||||
task->event.handler = ngx_http_copy_thread_event_handler;
|
||||
|
||||
if (ngx_thread_task_post(tp, task) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->main->blocked++;
|
||||
r->aio = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_copy_thread_event_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_http_request_t *r;
|
||||
|
||||
r = ev->data;
|
||||
|
||||
r->main->blocked--;
|
||||
r->aio = 0;
|
||||
|
||||
r->connection->write->handler(r->connection->write);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_copy_filter_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_copy_filter_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->bufs.num = 0;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_copy_filter_conf_t *prev = parent;
|
||||
ngx_http_copy_filter_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_copy_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_copy_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,600 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_CORE_H_INCLUDED_
|
||||
#define _NGX_HTTP_CORE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#if (NGX_THREADS)
|
||||
#include <ngx_thread_pool.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_HTTP_GZIP_PROXIED_OFF 0x0002
|
||||
#define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004
|
||||
#define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008
|
||||
#define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010
|
||||
#define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020
|
||||
#define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040
|
||||
#define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080
|
||||
#define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100
|
||||
#define NGX_HTTP_GZIP_PROXIED_ANY 0x0200
|
||||
|
||||
|
||||
#define NGX_HTTP_AIO_OFF 0
|
||||
#define NGX_HTTP_AIO_ON 1
|
||||
#define NGX_HTTP_AIO_THREADS 2
|
||||
|
||||
|
||||
#define NGX_HTTP_SATISFY_ALL 0
|
||||
#define NGX_HTTP_SATISFY_ANY 1
|
||||
|
||||
|
||||
#define NGX_HTTP_LINGERING_OFF 0
|
||||
#define NGX_HTTP_LINGERING_ON 1
|
||||
#define NGX_HTTP_LINGERING_ALWAYS 2
|
||||
|
||||
|
||||
#define NGX_HTTP_IMS_OFF 0
|
||||
#define NGX_HTTP_IMS_EXACT 1
|
||||
#define NGX_HTTP_IMS_BEFORE 2
|
||||
|
||||
|
||||
#define NGX_HTTP_KEEPALIVE_DISABLE_NONE 0x0002
|
||||
#define NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 0x0004
|
||||
#define NGX_HTTP_KEEPALIVE_DISABLE_SAFARI 0x0008
|
||||
|
||||
|
||||
typedef struct ngx_http_location_tree_node_s ngx_http_location_tree_node_t;
|
||||
typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct sockaddr sockaddr;
|
||||
struct sockaddr_in sockaddr_in;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 sockaddr_in6;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
struct sockaddr_un sockaddr_un;
|
||||
#endif
|
||||
u_char sockaddr_data[NGX_SOCKADDRLEN];
|
||||
} u;
|
||||
|
||||
socklen_t socklen;
|
||||
|
||||
unsigned set:1;
|
||||
unsigned default_server:1;
|
||||
unsigned bind:1;
|
||||
unsigned wildcard:1;
|
||||
#if (NGX_HTTP_SSL)
|
||||
unsigned ssl:1;
|
||||
#endif
|
||||
#if (NGX_HTTP_SPDY)
|
||||
unsigned spdy:1;
|
||||
#endif
|
||||
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
|
||||
unsigned ipv6only:1;
|
||||
#endif
|
||||
unsigned so_keepalive:2;
|
||||
unsigned proxy_protocol:1;
|
||||
|
||||
int backlog;
|
||||
int rcvbuf;
|
||||
int sndbuf;
|
||||
#if (NGX_HAVE_SETFIB)
|
||||
int setfib;
|
||||
#endif
|
||||
#if (NGX_HAVE_TCP_FASTOPEN)
|
||||
int fastopen;
|
||||
#endif
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
int tcp_keepidle;
|
||||
int tcp_keepintvl;
|
||||
int tcp_keepcnt;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
|
||||
char *accept_filter;
|
||||
#endif
|
||||
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
|
||||
ngx_uint_t deferred_accept;
|
||||
#endif
|
||||
|
||||
u_char addr[NGX_SOCKADDR_STRLEN + 1];
|
||||
} ngx_http_listen_opt_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
NGX_HTTP_POST_READ_PHASE = 0,
|
||||
|
||||
NGX_HTTP_SERVER_REWRITE_PHASE,
|
||||
|
||||
NGX_HTTP_FIND_CONFIG_PHASE,
|
||||
NGX_HTTP_REWRITE_PHASE,
|
||||
NGX_HTTP_POST_REWRITE_PHASE,
|
||||
|
||||
NGX_HTTP_PREACCESS_PHASE,
|
||||
|
||||
NGX_HTTP_ACCESS_PHASE,
|
||||
NGX_HTTP_POST_ACCESS_PHASE,
|
||||
|
||||
NGX_HTTP_TRY_FILES_PHASE,
|
||||
NGX_HTTP_CONTENT_PHASE,
|
||||
|
||||
NGX_HTTP_LOG_PHASE
|
||||
} ngx_http_phases;
|
||||
|
||||
typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
|
||||
struct ngx_http_phase_handler_s {
|
||||
ngx_http_phase_handler_pt checker;
|
||||
ngx_http_handler_pt handler;
|
||||
ngx_uint_t next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_phase_handler_t *handlers;
|
||||
ngx_uint_t server_rewrite_index;
|
||||
ngx_uint_t location_rewrite_index;
|
||||
} ngx_http_phase_engine_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t handlers;
|
||||
} ngx_http_phase_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
|
||||
|
||||
ngx_http_phase_engine_t phase_engine;
|
||||
|
||||
ngx_hash_t headers_in_hash;
|
||||
|
||||
ngx_hash_t variables_hash;
|
||||
|
||||
ngx_array_t variables; /* ngx_http_variable_t */
|
||||
ngx_uint_t ncaptures;
|
||||
|
||||
ngx_uint_t server_names_hash_max_size;
|
||||
ngx_uint_t server_names_hash_bucket_size;
|
||||
|
||||
ngx_uint_t variables_hash_max_size;
|
||||
ngx_uint_t variables_hash_bucket_size;
|
||||
|
||||
ngx_hash_keys_arrays_t *variables_keys;
|
||||
|
||||
ngx_array_t *ports;
|
||||
|
||||
ngx_uint_t try_files; /* unsigned try_files:1 */
|
||||
|
||||
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
|
||||
} ngx_http_core_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* array of the ngx_http_server_name_t, "server_name" directive */
|
||||
ngx_array_t server_names;
|
||||
|
||||
/* server ctx */
|
||||
ngx_http_conf_ctx_t *ctx;
|
||||
|
||||
ngx_str_t server_name;
|
||||
|
||||
size_t connection_pool_size;
|
||||
size_t request_pool_size;
|
||||
size_t client_header_buffer_size;
|
||||
|
||||
ngx_bufs_t large_client_header_buffers;
|
||||
|
||||
ngx_msec_t client_header_timeout;
|
||||
|
||||
ngx_flag_t ignore_invalid_headers;
|
||||
ngx_flag_t merge_slashes;
|
||||
ngx_flag_t underscores_in_headers;
|
||||
|
||||
unsigned listen:1;
|
||||
#if (NGX_PCRE)
|
||||
unsigned captures:1;
|
||||
#endif
|
||||
|
||||
ngx_http_core_loc_conf_t **named_locations;
|
||||
} ngx_http_core_srv_conf_t;
|
||||
|
||||
|
||||
/* list of structures to find core_srv_conf quickly at run time */
|
||||
|
||||
|
||||
typedef struct {
|
||||
#if (NGX_PCRE)
|
||||
ngx_http_regex_t *regex;
|
||||
#endif
|
||||
ngx_http_core_srv_conf_t *server; /* virtual name server conf */
|
||||
ngx_str_t name;
|
||||
} ngx_http_server_name_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_combined_t names;
|
||||
|
||||
ngx_uint_t nregex;
|
||||
ngx_http_server_name_t *regex;
|
||||
} ngx_http_virtual_names_t;
|
||||
|
||||
|
||||
struct ngx_http_addr_conf_s {
|
||||
/* the default server configuration for this address:port */
|
||||
ngx_http_core_srv_conf_t *default_server;
|
||||
|
||||
ngx_http_virtual_names_t *virtual_names;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
unsigned ssl:1;
|
||||
#endif
|
||||
#if (NGX_HTTP_SPDY)
|
||||
unsigned spdy:1;
|
||||
#endif
|
||||
unsigned proxy_protocol:1;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
in_addr_t addr;
|
||||
ngx_http_addr_conf_t conf;
|
||||
} ngx_http_in_addr_t;
|
||||
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
typedef struct {
|
||||
struct in6_addr addr6;
|
||||
ngx_http_addr_conf_t conf;
|
||||
} ngx_http_in6_addr_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* ngx_http_in_addr_t or ngx_http_in6_addr_t */
|
||||
void *addrs;
|
||||
ngx_uint_t naddrs;
|
||||
} ngx_http_port_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t family;
|
||||
in_port_t port;
|
||||
ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
|
||||
} ngx_http_conf_port_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_listen_opt_t opt;
|
||||
|
||||
ngx_hash_t hash;
|
||||
ngx_hash_wildcard_t *wc_head;
|
||||
ngx_hash_wildcard_t *wc_tail;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_uint_t nregex;
|
||||
ngx_http_server_name_t *regex;
|
||||
#endif
|
||||
|
||||
/* the default server configuration for this address:port */
|
||||
ngx_http_core_srv_conf_t *default_server;
|
||||
ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */
|
||||
} ngx_http_conf_addr_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t status;
|
||||
ngx_int_t overwrite;
|
||||
ngx_http_complex_value_t value;
|
||||
ngx_str_t args;
|
||||
} ngx_http_err_page_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *lengths;
|
||||
ngx_array_t *values;
|
||||
ngx_str_t name;
|
||||
|
||||
unsigned code:10;
|
||||
unsigned test_dir:1;
|
||||
} ngx_http_try_file_t;
|
||||
|
||||
|
||||
struct ngx_http_core_loc_conf_s {
|
||||
ngx_str_t name; /* location name */
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_http_regex_t *regex;
|
||||
#endif
|
||||
|
||||
unsigned noname:1; /* "if () {}" block or limit_except */
|
||||
unsigned lmt_excpt:1;
|
||||
unsigned named:1;
|
||||
|
||||
unsigned exact_match:1;
|
||||
unsigned noregex:1;
|
||||
|
||||
unsigned auto_redirect:1;
|
||||
#if (NGX_HTTP_GZIP)
|
||||
unsigned gzip_disable_msie6:2;
|
||||
#if (NGX_HTTP_DEGRADATION)
|
||||
unsigned gzip_disable_degradation:2;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ngx_http_location_tree_node_t *static_locations;
|
||||
#if (NGX_PCRE)
|
||||
ngx_http_core_loc_conf_t **regex_locations;
|
||||
#endif
|
||||
|
||||
/* pointer to the modules' loc_conf */
|
||||
void **loc_conf;
|
||||
|
||||
uint32_t limit_except;
|
||||
void **limit_except_loc_conf;
|
||||
|
||||
ngx_http_handler_pt handler;
|
||||
|
||||
/* location name length for inclusive location with inherited alias */
|
||||
size_t alias;
|
||||
ngx_str_t root; /* root, alias */
|
||||
ngx_str_t post_action;
|
||||
|
||||
ngx_array_t *root_lengths;
|
||||
ngx_array_t *root_values;
|
||||
|
||||
ngx_array_t *types;
|
||||
ngx_hash_t types_hash;
|
||||
ngx_str_t default_type;
|
||||
|
||||
off_t client_max_body_size; /* client_max_body_size */
|
||||
off_t directio; /* directio */
|
||||
off_t directio_alignment; /* directio_alignment */
|
||||
|
||||
size_t client_body_buffer_size; /* client_body_buffer_size */
|
||||
size_t send_lowat; /* send_lowat */
|
||||
size_t postpone_output; /* postpone_output */
|
||||
size_t limit_rate; /* limit_rate */
|
||||
size_t limit_rate_after; /* limit_rate_after */
|
||||
size_t sendfile_max_chunk; /* sendfile_max_chunk */
|
||||
size_t read_ahead; /* read_ahead */
|
||||
|
||||
ngx_msec_t client_body_timeout; /* client_body_timeout */
|
||||
ngx_msec_t send_timeout; /* send_timeout */
|
||||
ngx_msec_t keepalive_timeout; /* keepalive_timeout */
|
||||
ngx_msec_t lingering_time; /* lingering_time */
|
||||
ngx_msec_t lingering_timeout; /* lingering_timeout */
|
||||
ngx_msec_t resolver_timeout; /* resolver_timeout */
|
||||
|
||||
ngx_resolver_t *resolver; /* resolver */
|
||||
|
||||
time_t keepalive_header; /* keepalive_timeout */
|
||||
|
||||
ngx_uint_t keepalive_requests; /* keepalive_requests */
|
||||
ngx_uint_t keepalive_disable; /* keepalive_disable */
|
||||
ngx_uint_t satisfy; /* satisfy */
|
||||
ngx_uint_t lingering_close; /* lingering_close */
|
||||
ngx_uint_t if_modified_since; /* if_modified_since */
|
||||
ngx_uint_t max_ranges; /* max_ranges */
|
||||
ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */
|
||||
|
||||
ngx_flag_t client_body_in_single_buffer;
|
||||
/* client_body_in_singe_buffer */
|
||||
ngx_flag_t internal; /* internal */
|
||||
ngx_flag_t sendfile; /* sendfile */
|
||||
ngx_flag_t aio; /* aio */
|
||||
ngx_flag_t tcp_nopush; /* tcp_nopush */
|
||||
ngx_flag_t tcp_nodelay; /* tcp_nodelay */
|
||||
ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */
|
||||
ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */
|
||||
ngx_flag_t port_in_redirect; /* port_in_redirect */
|
||||
ngx_flag_t msie_padding; /* msie_padding */
|
||||
ngx_flag_t msie_refresh; /* msie_refresh */
|
||||
ngx_flag_t log_not_found; /* log_not_found */
|
||||
ngx_flag_t log_subrequest; /* log_subrequest */
|
||||
ngx_flag_t recursive_error_pages; /* recursive_error_pages */
|
||||
ngx_flag_t server_tokens; /* server_tokens */
|
||||
ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */
|
||||
ngx_flag_t etag; /* etag */
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
ngx_flag_t gzip_vary; /* gzip_vary */
|
||||
|
||||
ngx_uint_t gzip_http_version; /* gzip_http_version */
|
||||
ngx_uint_t gzip_proxied; /* gzip_proxied */
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_array_t *gzip_disable; /* gzip_disable */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (NGX_THREADS)
|
||||
ngx_thread_pool_t *thread_pool;
|
||||
ngx_http_complex_value_t *thread_pool_value;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_OPENAT)
|
||||
ngx_uint_t disable_symlinks; /* disable_symlinks */
|
||||
ngx_http_complex_value_t *disable_symlinks_from;
|
||||
#endif
|
||||
|
||||
ngx_array_t *error_pages; /* error_page */
|
||||
ngx_http_try_file_t *try_files; /* try_files */
|
||||
|
||||
ngx_path_t *client_body_temp_path; /* client_body_temp_path */
|
||||
|
||||
ngx_open_file_cache_t *open_file_cache;
|
||||
time_t open_file_cache_valid;
|
||||
ngx_uint_t open_file_cache_min_uses;
|
||||
ngx_flag_t open_file_cache_errors;
|
||||
ngx_flag_t open_file_cache_events;
|
||||
|
||||
ngx_log_t *error_log;
|
||||
|
||||
ngx_uint_t types_hash_max_size;
|
||||
ngx_uint_t types_hash_bucket_size;
|
||||
|
||||
ngx_queue_t *locations;
|
||||
|
||||
#if 0
|
||||
ngx_http_core_loc_conf_t *prev_location;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_queue_t queue;
|
||||
ngx_http_core_loc_conf_t *exact;
|
||||
ngx_http_core_loc_conf_t *inclusive;
|
||||
ngx_str_t *name;
|
||||
u_char *file_name;
|
||||
ngx_uint_t line;
|
||||
ngx_queue_t list;
|
||||
} ngx_http_location_queue_t;
|
||||
|
||||
|
||||
struct ngx_http_location_tree_node_s {
|
||||
ngx_http_location_tree_node_t *left;
|
||||
ngx_http_location_tree_node_t *right;
|
||||
ngx_http_location_tree_node_t *tree;
|
||||
|
||||
ngx_http_core_loc_conf_t *exact;
|
||||
ngx_http_core_loc_conf_t *inclusive;
|
||||
|
||||
u_char auto_redirect;
|
||||
u_char len;
|
||||
u_char name[1];
|
||||
};
|
||||
|
||||
|
||||
void ngx_http_core_run_phases(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
|
||||
ngx_http_phase_handler_t *ph);
|
||||
|
||||
|
||||
void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
|
||||
ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
|
||||
void ngx_http_set_exten(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_set_etag(ngx_http_request_t *r);
|
||||
void ngx_http_weak_etag(ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
|
||||
ngx_str_t *ct, ngx_http_complex_value_t *cv);
|
||||
u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
|
||||
size_t *root_length, size_t reserved);
|
||||
ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
|
||||
#if (NGX_HTTP_GZIP)
|
||||
ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r);
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
|
||||
ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr,
|
||||
ngx_http_post_subrequest_t *psr, ngx_uint_t flags);
|
||||
ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,
|
||||
ngx_str_t *uri, ngx_str_t *args);
|
||||
ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);
|
||||
|
||||
|
||||
ngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size);
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
|
||||
typedef ngx_int_t (*ngx_http_output_body_filter_pt)
|
||||
(ngx_http_request_t *r, ngx_chain_t *chain);
|
||||
typedef ngx_int_t (*ngx_http_request_body_filter_pt)
|
||||
(ngx_http_request_t *r, ngx_chain_t *chain);
|
||||
|
||||
|
||||
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain);
|
||||
ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);
|
||||
ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
|
||||
ngx_chain_t *chain);
|
||||
|
||||
|
||||
ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,
|
||||
ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);
|
||||
|
||||
ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
|
||||
ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
|
||||
int recursive);
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_core_module;
|
||||
|
||||
extern ngx_uint_t ngx_http_max_module;
|
||||
|
||||
extern ngx_str_t ngx_http_core_get_method;
|
||||
|
||||
|
||||
#define ngx_http_clear_content_length(r) \
|
||||
\
|
||||
r->headers_out.content_length_n = -1; \
|
||||
if (r->headers_out.content_length) { \
|
||||
r->headers_out.content_length->hash = 0; \
|
||||
r->headers_out.content_length = NULL; \
|
||||
}
|
||||
|
||||
#define ngx_http_clear_accept_ranges(r) \
|
||||
\
|
||||
r->allow_ranges = 0; \
|
||||
if (r->headers_out.accept_ranges) { \
|
||||
r->headers_out.accept_ranges->hash = 0; \
|
||||
r->headers_out.accept_ranges = NULL; \
|
||||
}
|
||||
|
||||
#define ngx_http_clear_last_modified(r) \
|
||||
\
|
||||
r->headers_out.last_modified_time = -1; \
|
||||
if (r->headers_out.last_modified) { \
|
||||
r->headers_out.last_modified->hash = 0; \
|
||||
r->headers_out.last_modified = NULL; \
|
||||
}
|
||||
|
||||
#define ngx_http_clear_location(r) \
|
||||
\
|
||||
if (r->headers_out.location) { \
|
||||
r->headers_out.location->hash = 0; \
|
||||
r->headers_out.location = NULL; \
|
||||
}
|
||||
|
||||
#define ngx_http_clear_etag(r) \
|
||||
\
|
||||
if (r->headers_out.etag) { \
|
||||
r->headers_out.etag->hash = 0; \
|
||||
r->headers_out.etag = NULL; \
|
||||
}
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,633 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <nginx.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_header_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_header_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL, /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_header_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_header_filter_module_ctx, /* module context */
|
||||
NULL, /* 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 char ngx_http_server_string[] = "Server: nginx" CRLF;
|
||||
static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_status_lines[] = {
|
||||
|
||||
ngx_string("200 OK"),
|
||||
ngx_string("201 Created"),
|
||||
ngx_string("202 Accepted"),
|
||||
ngx_null_string, /* "203 Non-Authoritative Information" */
|
||||
ngx_string("204 No Content"),
|
||||
ngx_null_string, /* "205 Reset Content" */
|
||||
ngx_string("206 Partial Content"),
|
||||
|
||||
/* ngx_null_string, */ /* "207 Multi-Status" */
|
||||
|
||||
#define NGX_HTTP_LAST_2XX 207
|
||||
#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 200)
|
||||
|
||||
/* ngx_null_string, */ /* "300 Multiple Choices" */
|
||||
|
||||
ngx_string("301 Moved Permanently"),
|
||||
ngx_string("302 Moved Temporarily"),
|
||||
ngx_string("303 See Other"),
|
||||
ngx_string("304 Not Modified"),
|
||||
ngx_null_string, /* "305 Use Proxy" */
|
||||
ngx_null_string, /* "306 unused" */
|
||||
ngx_string("307 Temporary Redirect"),
|
||||
|
||||
#define NGX_HTTP_LAST_3XX 308
|
||||
#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
|
||||
|
||||
ngx_string("400 Bad Request"),
|
||||
ngx_string("401 Unauthorized"),
|
||||
ngx_string("402 Payment Required"),
|
||||
ngx_string("403 Forbidden"),
|
||||
ngx_string("404 Not Found"),
|
||||
ngx_string("405 Not Allowed"),
|
||||
ngx_string("406 Not Acceptable"),
|
||||
ngx_null_string, /* "407 Proxy Authentication Required" */
|
||||
ngx_string("408 Request Time-out"),
|
||||
ngx_string("409 Conflict"),
|
||||
ngx_string("410 Gone"),
|
||||
ngx_string("411 Length Required"),
|
||||
ngx_string("412 Precondition Failed"),
|
||||
ngx_string("413 Request Entity Too Large"),
|
||||
ngx_string("414 Request-URI Too Large"),
|
||||
ngx_string("415 Unsupported Media Type"),
|
||||
ngx_string("416 Requested Range Not Satisfiable"),
|
||||
|
||||
/* ngx_null_string, */ /* "417 Expectation Failed" */
|
||||
/* ngx_null_string, */ /* "418 unused" */
|
||||
/* ngx_null_string, */ /* "419 unused" */
|
||||
/* ngx_null_string, */ /* "420 unused" */
|
||||
/* ngx_null_string, */ /* "421 unused" */
|
||||
/* ngx_null_string, */ /* "422 Unprocessable Entity" */
|
||||
/* ngx_null_string, */ /* "423 Locked" */
|
||||
/* ngx_null_string, */ /* "424 Failed Dependency" */
|
||||
|
||||
#define NGX_HTTP_LAST_4XX 417
|
||||
#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
|
||||
|
||||
ngx_string("500 Internal Server Error"),
|
||||
ngx_string("501 Not Implemented"),
|
||||
ngx_string("502 Bad Gateway"),
|
||||
ngx_string("503 Service Temporarily Unavailable"),
|
||||
ngx_string("504 Gateway Time-out"),
|
||||
|
||||
ngx_null_string, /* "505 HTTP Version Not Supported" */
|
||||
ngx_null_string, /* "506 Variant Also Negotiates" */
|
||||
ngx_string("507 Insufficient Storage"),
|
||||
/* ngx_null_string, */ /* "508 unused" */
|
||||
/* ngx_null_string, */ /* "509 unused" */
|
||||
/* ngx_null_string, */ /* "510 Not Extended" */
|
||||
|
||||
#define NGX_HTTP_LAST_5XX 508
|
||||
|
||||
};
|
||||
|
||||
|
||||
ngx_http_header_out_t ngx_http_headers_out[] = {
|
||||
{ ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) },
|
||||
{ ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) },
|
||||
{ ngx_string("Content-Length"),
|
||||
offsetof(ngx_http_headers_out_t, content_length) },
|
||||
{ ngx_string("Content-Encoding"),
|
||||
offsetof(ngx_http_headers_out_t, content_encoding) },
|
||||
{ ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) },
|
||||
{ ngx_string("Last-Modified"),
|
||||
offsetof(ngx_http_headers_out_t, last_modified) },
|
||||
{ ngx_string("Accept-Ranges"),
|
||||
offsetof(ngx_http_headers_out_t, accept_ranges) },
|
||||
{ ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) },
|
||||
{ ngx_string("Cache-Control"),
|
||||
offsetof(ngx_http_headers_out_t, cache_control) },
|
||||
{ ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) },
|
||||
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_header_filter(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_str_t host, *status_line;
|
||||
ngx_buf_t *b;
|
||||
ngx_uint_t status, i, port;
|
||||
ngx_chain_t out;
|
||||
ngx_list_part_t *part;
|
||||
ngx_table_elt_t *header;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_http_core_srv_conf_t *cscf;
|
||||
struct sockaddr_in *sin;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
u_char addr[NGX_SOCKADDR_STRLEN];
|
||||
|
||||
if (r->header_sent) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
r->header_sent = 1;
|
||||
|
||||
if (r != r->main) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (r->http_version < NGX_HTTP_VERSION_10) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (r->method == NGX_HTTP_HEAD) {
|
||||
r->header_only = 1;
|
||||
}
|
||||
|
||||
if (r->headers_out.last_modified_time != -1) {
|
||||
if (r->headers_out.status != NGX_HTTP_OK
|
||||
&& r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
|
||||
&& r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
|
||||
{
|
||||
r->headers_out.last_modified_time = -1;
|
||||
r->headers_out.last_modified = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
|
||||
/* the end of the header */
|
||||
+ sizeof(CRLF) - 1;
|
||||
|
||||
/* status line */
|
||||
|
||||
if (r->headers_out.status_line.len) {
|
||||
len += r->headers_out.status_line.len;
|
||||
status_line = &r->headers_out.status_line;
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
status = 0;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
|
||||
status = r->headers_out.status;
|
||||
|
||||
if (status >= NGX_HTTP_OK
|
||||
&& status < NGX_HTTP_LAST_2XX)
|
||||
{
|
||||
/* 2XX */
|
||||
|
||||
if (status == NGX_HTTP_NO_CONTENT) {
|
||||
r->header_only = 1;
|
||||
ngx_str_null(&r->headers_out.content_type);
|
||||
r->headers_out.last_modified_time = -1;
|
||||
r->headers_out.last_modified = NULL;
|
||||
r->headers_out.content_length = NULL;
|
||||
r->headers_out.content_length_n = -1;
|
||||
}
|
||||
|
||||
status -= NGX_HTTP_OK;
|
||||
status_line = &ngx_http_status_lines[status];
|
||||
len += ngx_http_status_lines[status].len;
|
||||
|
||||
} else if (status >= NGX_HTTP_MOVED_PERMANENTLY
|
||||
&& status < NGX_HTTP_LAST_3XX)
|
||||
{
|
||||
/* 3XX */
|
||||
|
||||
if (status == NGX_HTTP_NOT_MODIFIED) {
|
||||
r->header_only = 1;
|
||||
}
|
||||
|
||||
status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
|
||||
status_line = &ngx_http_status_lines[status];
|
||||
len += ngx_http_status_lines[status].len;
|
||||
|
||||
} else if (status >= NGX_HTTP_BAD_REQUEST
|
||||
&& status < NGX_HTTP_LAST_4XX)
|
||||
{
|
||||
/* 4XX */
|
||||
status = status - NGX_HTTP_BAD_REQUEST
|
||||
+ NGX_HTTP_OFF_4XX;
|
||||
|
||||
status_line = &ngx_http_status_lines[status];
|
||||
len += ngx_http_status_lines[status].len;
|
||||
|
||||
} else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
|
||||
&& status < NGX_HTTP_LAST_5XX)
|
||||
{
|
||||
/* 5XX */
|
||||
status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
|
||||
+ NGX_HTTP_OFF_5XX;
|
||||
|
||||
status_line = &ngx_http_status_lines[status];
|
||||
len += ngx_http_status_lines[status].len;
|
||||
|
||||
} else {
|
||||
len += NGX_INT_T_LEN + 1 /* SP */;
|
||||
status_line = NULL;
|
||||
}
|
||||
|
||||
if (status_line && status_line->len == 0) {
|
||||
status = r->headers_out.status;
|
||||
len += NGX_INT_T_LEN + 1 /* SP */;
|
||||
status_line = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (r->headers_out.server == NULL) {
|
||||
len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:
|
||||
sizeof(ngx_http_server_string) - 1;
|
||||
}
|
||||
|
||||
if (r->headers_out.date == NULL) {
|
||||
len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
|
||||
}
|
||||
|
||||
if (r->headers_out.content_type.len) {
|
||||
len += sizeof("Content-Type: ") - 1
|
||||
+ r->headers_out.content_type.len + 2;
|
||||
|
||||
if (r->headers_out.content_type_len == r->headers_out.content_type.len
|
||||
&& r->headers_out.charset.len)
|
||||
{
|
||||
len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->headers_out.content_length == NULL
|
||||
&& r->headers_out.content_length_n >= 0)
|
||||
{
|
||||
len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2;
|
||||
}
|
||||
|
||||
if (r->headers_out.last_modified == NULL
|
||||
&& r->headers_out.last_modified_time != -1)
|
||||
{
|
||||
len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
|
||||
}
|
||||
|
||||
c = r->connection;
|
||||
|
||||
if (r->headers_out.location
|
||||
&& r->headers_out.location->value.len
|
||||
&& r->headers_out.location->value.data[0] == '/')
|
||||
{
|
||||
r->headers_out.location->hash = 0;
|
||||
|
||||
if (clcf->server_name_in_redirect) {
|
||||
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
||||
host = cscf->server_name;
|
||||
|
||||
} else if (r->headers_in.server.len) {
|
||||
host = r->headers_in.server;
|
||||
|
||||
} else {
|
||||
host.len = NGX_SOCKADDR_STRLEN;
|
||||
host.data = addr;
|
||||
|
||||
if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (c->local_sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
|
||||
port = ntohs(sin6->sin6_port);
|
||||
break;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
case AF_UNIX:
|
||||
port = 0;
|
||||
break;
|
||||
#endif
|
||||
default: /* AF_INET */
|
||||
sin = (struct sockaddr_in *) c->local_sockaddr;
|
||||
port = ntohs(sin->sin_port);
|
||||
break;
|
||||
}
|
||||
|
||||
len += sizeof("Location: https://") - 1
|
||||
+ host.len
|
||||
+ r->headers_out.location->value.len + 2;
|
||||
|
||||
if (clcf->port_in_redirect) {
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
if (c->ssl)
|
||||
port = (port == 443) ? 0 : port;
|
||||
else
|
||||
#endif
|
||||
port = (port == 80) ? 0 : port;
|
||||
|
||||
} else {
|
||||
port = 0;
|
||||
}
|
||||
|
||||
if (port) {
|
||||
len += sizeof(":65535") - 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
ngx_str_null(&host);
|
||||
port = 0;
|
||||
}
|
||||
|
||||
if (r->chunked) {
|
||||
len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
|
||||
}
|
||||
|
||||
if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
|
||||
len += sizeof("Connection: upgrade" CRLF) - 1;
|
||||
|
||||
} else if (r->keepalive) {
|
||||
len += sizeof("Connection: keep-alive" CRLF) - 1;
|
||||
|
||||
/*
|
||||
* MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header.
|
||||
* MSIE keeps the connection alive for about 60-65 seconds.
|
||||
* Opera keeps the connection alive very long.
|
||||
* Mozilla keeps the connection alive for N plus about 1-10 seconds.
|
||||
* Konqueror keeps the connection alive for about N seconds.
|
||||
*/
|
||||
|
||||
if (clcf->keepalive_header) {
|
||||
len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2;
|
||||
}
|
||||
|
||||
} else {
|
||||
len += sizeof("Connection: close" CRLF) - 1;
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
if (r->gzip_vary) {
|
||||
if (clcf->gzip_vary) {
|
||||
len += sizeof("Vary: Accept-Encoding" CRLF) - 1;
|
||||
|
||||
} else {
|
||||
r->gzip_vary = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
part = &r->headers_out.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].hash == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
|
||||
+ sizeof(CRLF) - 1;
|
||||
}
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, len);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* "HTTP/1.x " */
|
||||
b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
|
||||
|
||||
/* status line */
|
||||
if (status_line) {
|
||||
b->last = ngx_copy(b->last, status_line->data, status_line->len);
|
||||
|
||||
} else {
|
||||
b->last = ngx_sprintf(b->last, "%03ui ", status);
|
||||
}
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
|
||||
if (r->headers_out.server == NULL) {
|
||||
if (clcf->server_tokens) {
|
||||
p = (u_char *) ngx_http_server_full_string;
|
||||
len = sizeof(ngx_http_server_full_string) - 1;
|
||||
|
||||
} else {
|
||||
p = (u_char *) ngx_http_server_string;
|
||||
len = sizeof(ngx_http_server_string) - 1;
|
||||
}
|
||||
|
||||
b->last = ngx_cpymem(b->last, p, len);
|
||||
}
|
||||
|
||||
if (r->headers_out.date == NULL) {
|
||||
b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1);
|
||||
b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
|
||||
ngx_cached_http_time.len);
|
||||
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
if (r->headers_out.content_type.len) {
|
||||
b->last = ngx_cpymem(b->last, "Content-Type: ",
|
||||
sizeof("Content-Type: ") - 1);
|
||||
p = b->last;
|
||||
b->last = ngx_copy(b->last, r->headers_out.content_type.data,
|
||||
r->headers_out.content_type.len);
|
||||
|
||||
if (r->headers_out.content_type_len == r->headers_out.content_type.len
|
||||
&& r->headers_out.charset.len)
|
||||
{
|
||||
b->last = ngx_cpymem(b->last, "; charset=",
|
||||
sizeof("; charset=") - 1);
|
||||
b->last = ngx_copy(b->last, r->headers_out.charset.data,
|
||||
r->headers_out.charset.len);
|
||||
|
||||
/* update r->headers_out.content_type for possible logging */
|
||||
|
||||
r->headers_out.content_type.len = b->last - p;
|
||||
r->headers_out.content_type.data = p;
|
||||
}
|
||||
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
if (r->headers_out.content_length == NULL
|
||||
&& r->headers_out.content_length_n >= 0)
|
||||
{
|
||||
b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,
|
||||
r->headers_out.content_length_n);
|
||||
}
|
||||
|
||||
if (r->headers_out.last_modified == NULL
|
||||
&& r->headers_out.last_modified_time != -1)
|
||||
{
|
||||
b->last = ngx_cpymem(b->last, "Last-Modified: ",
|
||||
sizeof("Last-Modified: ") - 1);
|
||||
b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);
|
||||
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
if (host.data) {
|
||||
|
||||
p = b->last + sizeof("Location: ") - 1;
|
||||
|
||||
b->last = ngx_cpymem(b->last, "Location: http",
|
||||
sizeof("Location: http") - 1);
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
if (c->ssl) {
|
||||
*b->last++ ='s';
|
||||
}
|
||||
#endif
|
||||
|
||||
*b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
|
||||
b->last = ngx_copy(b->last, host.data, host.len);
|
||||
|
||||
if (port) {
|
||||
b->last = ngx_sprintf(b->last, ":%ui", port);
|
||||
}
|
||||
|
||||
b->last = ngx_copy(b->last, r->headers_out.location->value.data,
|
||||
r->headers_out.location->value.len);
|
||||
|
||||
/* update r->headers_out.location->value for possible logging */
|
||||
|
||||
r->headers_out.location->value.len = b->last - p;
|
||||
r->headers_out.location->value.data = p;
|
||||
ngx_str_set(&r->headers_out.location->key, "Location");
|
||||
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
if (r->chunked) {
|
||||
b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF,
|
||||
sizeof("Transfer-Encoding: chunked" CRLF) - 1);
|
||||
}
|
||||
|
||||
if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
|
||||
b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
|
||||
sizeof("Connection: upgrade" CRLF) - 1);
|
||||
|
||||
} else if (r->keepalive) {
|
||||
b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
|
||||
sizeof("Connection: keep-alive" CRLF) - 1);
|
||||
|
||||
if (clcf->keepalive_header) {
|
||||
b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,
|
||||
clcf->keepalive_header);
|
||||
}
|
||||
|
||||
} else {
|
||||
b->last = ngx_cpymem(b->last, "Connection: close" CRLF,
|
||||
sizeof("Connection: close" CRLF) - 1);
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
if (r->gzip_vary) {
|
||||
b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,
|
||||
sizeof("Vary: Accept-Encoding" CRLF) - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
part = &r->headers_out.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].hash == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
|
||||
*b->last++ = ':'; *b->last++ = ' ';
|
||||
|
||||
b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"%*s", (size_t) (b->last - b->pos), b->pos);
|
||||
|
||||
/* the end of HTTP header */
|
||||
*b->last++ = CR; *b->last++ = LF;
|
||||
|
||||
r->header_size = b->last - b->pos;
|
||||
|
||||
if (r->header_only) {
|
||||
b->last_buf = 1;
|
||||
}
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
return ngx_http_write_filter(r, &out);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_header_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_top_header_filter = ngx_http_header_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
time_t
|
||||
ngx_http_parse_time(u_char *value, size_t len)
|
||||
{
|
||||
u_char *p, *end;
|
||||
ngx_int_t month;
|
||||
ngx_uint_t day, year, hour, min, sec;
|
||||
uint64_t time;
|
||||
enum {
|
||||
no = 0,
|
||||
rfc822, /* Tue, 10 Nov 2002 23:50:13 */
|
||||
rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
|
||||
isoc /* Tue Dec 10 23:50:13 2002 */
|
||||
} fmt;
|
||||
|
||||
fmt = 0;
|
||||
end = value + len;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
day = 32;
|
||||
year = 2038;
|
||||
#endif
|
||||
|
||||
for (p = value; p < end; p++) {
|
||||
if (*p == ',') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p == ' ') {
|
||||
fmt = isoc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (p++; p < end; p++)
|
||||
if (*p != ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (end - p < 18) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (fmt != isoc) {
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
day = (*p - '0') * 10 + *(p + 1) - '0';
|
||||
p += 2;
|
||||
|
||||
if (*p == ' ') {
|
||||
if (end - p < 18) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
fmt = rfc822;
|
||||
|
||||
} else if (*p == '-') {
|
||||
fmt = rfc850;
|
||||
|
||||
} else {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
switch (*p) {
|
||||
|
||||
case 'J':
|
||||
month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
month = 1;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
month = *(p + 2) == 'r' ? 2 : 4;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
month = *(p + 1) == 'p' ? 3 : 7;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
month = 8;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
month = 9;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
month = 10;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
month = 11;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p += 3;
|
||||
|
||||
if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
if (fmt == rfc822) {
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
|
||||
|| *(p + 2) < '0' || *(p + 2) > '9'
|
||||
|| *(p + 3) < '0' || *(p + 3) > '9')
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
|
||||
+ (*(p + 2) - '0') * 10 + *(p + 3) - '0';
|
||||
p += 4;
|
||||
|
||||
} else if (fmt == rfc850) {
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
year = (*p - '0') * 10 + *(p + 1) - '0';
|
||||
year += (year < 70) ? 2000 : 1900;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
if (fmt == isoc) {
|
||||
if (*p == ' ') {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
day = *p++ - '0';
|
||||
|
||||
if (*p != ' ') {
|
||||
if (*p < '0' || *p > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
day = day * 10 + *p++ - '0';
|
||||
}
|
||||
|
||||
if (end - p < 14) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p++ != ' ') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
hour = (*p - '0') * 10 + *(p + 1) - '0';
|
||||
p += 2;
|
||||
|
||||
if (*p++ != ':') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
min = (*p - '0') * 10 + *(p + 1) - '0';
|
||||
p += 2;
|
||||
|
||||
if (*p++ != ':') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
sec = (*p - '0') * 10 + *(p + 1) - '0';
|
||||
|
||||
if (fmt == isoc) {
|
||||
p += 2;
|
||||
|
||||
if (*p++ != ' ') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
|
||||
|| *(p + 2) < '0' || *(p + 2) > '9'
|
||||
|| *(p + 3) < '0' || *(p + 3) > '9')
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
|
||||
+ (*(p + 2) - '0') * 10 + *(p + 3) - '0';
|
||||
}
|
||||
|
||||
if (hour > 23 || min > 59 || sec > 59) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (day == 29 && month == 1) {
|
||||
if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else if (day > mday[month]) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* shift new year to March 1 and start months from 1 (not 0),
|
||||
* it is needed for Gauss' formula
|
||||
*/
|
||||
|
||||
if (--month <= 0) {
|
||||
month += 12;
|
||||
year -= 1;
|
||||
}
|
||||
|
||||
/* Gauss' formula for Gregorian days since March 1, 1 BC */
|
||||
|
||||
time = (uint64_t) (
|
||||
/* days in years including leap years since March 1, 1 BC */
|
||||
|
||||
365 * year + year / 4 - year / 100 + year / 400
|
||||
|
||||
/* days before the month */
|
||||
|
||||
+ 367 * month / 12 - 30
|
||||
|
||||
/* days before the day */
|
||||
|
||||
+ day - 1
|
||||
|
||||
/*
|
||||
* 719527 days were between March 1, 1 BC and March 1, 1970,
|
||||
* 31 and 28 days were in January and February 1970
|
||||
*/
|
||||
|
||||
- 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
|
||||
|
||||
#if (NGX_TIME_T_SIZE <= 4)
|
||||
|
||||
if (time > 0x7fffffff) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return (time_t) time;
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
|
||||
ngx_chain_t *in);
|
||||
static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_postpone_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_postpone_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_postpone_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_postpone_filter_module_ctx, /* module context */
|
||||
NULL, /* 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_http_output_body_filter_pt ngx_http_next_body_filter;
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_http_postponed_request_t *pr;
|
||||
|
||||
c = r->connection;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
|
||||
|
||||
if (r != c->data) {
|
||||
|
||||
if (in) {
|
||||
ngx_http_postpone_filter_add(r, in);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* TODO: SSI may pass NULL */
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"http postpone filter NULL inactive request");
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (r->postponed == NULL) {
|
||||
|
||||
if (in || c->buffered) {
|
||||
return ngx_http_next_body_filter(r->main, in);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (in) {
|
||||
ngx_http_postpone_filter_add(r, in);
|
||||
}
|
||||
|
||||
do {
|
||||
pr = r->postponed;
|
||||
|
||||
if (pr->request) {
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http postpone filter wake \"%V?%V\"",
|
||||
&pr->request->uri, &pr->request->args);
|
||||
|
||||
r->postponed = pr->next;
|
||||
|
||||
c->data = pr->request;
|
||||
|
||||
return ngx_http_post_request(pr->request, NULL);
|
||||
}
|
||||
|
||||
if (pr->out == NULL) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"http postpone filter NULL output");
|
||||
|
||||
} else {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http postpone filter output \"%V?%V\"",
|
||||
&r->uri, &r->args);
|
||||
|
||||
if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
r->postponed = pr->next;
|
||||
|
||||
} while (r->postponed);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
ngx_http_postponed_request_t *pr, **ppr;
|
||||
|
||||
if (r->postponed) {
|
||||
for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
|
||||
|
||||
if (pr->request == NULL) {
|
||||
goto found;
|
||||
}
|
||||
|
||||
ppr = &pr->next;
|
||||
|
||||
} else {
|
||||
ppr = &r->postponed;
|
||||
}
|
||||
|
||||
pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
|
||||
if (pr == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*ppr = pr;
|
||||
|
||||
pr->request = NULL;
|
||||
pr->out = NULL;
|
||||
pr->next = NULL;
|
||||
|
||||
found:
|
||||
|
||||
if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_postpone_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_next_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_postpone_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,604 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_
|
||||
#define _NGX_HTTP_REQUEST_H_INCLUDED_
|
||||
|
||||
|
||||
#define NGX_HTTP_MAX_URI_CHANGES 10
|
||||
#define NGX_HTTP_MAX_SUBREQUESTS 200
|
||||
|
||||
/* must be 2^n */
|
||||
#define NGX_HTTP_LC_HEADER_LEN 32
|
||||
|
||||
|
||||
#define NGX_HTTP_DISCARD_BUFFER_SIZE 4096
|
||||
#define NGX_HTTP_LINGERING_BUFFER_SIZE 4096
|
||||
|
||||
|
||||
#define NGX_HTTP_VERSION_9 9
|
||||
#define NGX_HTTP_VERSION_10 1000
|
||||
#define NGX_HTTP_VERSION_11 1001
|
||||
|
||||
#define NGX_HTTP_UNKNOWN 0x0001
|
||||
#define NGX_HTTP_GET 0x0002
|
||||
#define NGX_HTTP_HEAD 0x0004
|
||||
#define NGX_HTTP_POST 0x0008
|
||||
#define NGX_HTTP_PUT 0x0010
|
||||
#define NGX_HTTP_DELETE 0x0020
|
||||
#define NGX_HTTP_MKCOL 0x0040
|
||||
#define NGX_HTTP_COPY 0x0080
|
||||
#define NGX_HTTP_MOVE 0x0100
|
||||
#define NGX_HTTP_OPTIONS 0x0200
|
||||
#define NGX_HTTP_PROPFIND 0x0400
|
||||
#define NGX_HTTP_PROPPATCH 0x0800
|
||||
#define NGX_HTTP_LOCK 0x1000
|
||||
#define NGX_HTTP_UNLOCK 0x2000
|
||||
#define NGX_HTTP_PATCH 0x4000
|
||||
#define NGX_HTTP_TRACE 0x8000
|
||||
|
||||
#define NGX_HTTP_CONNECTION_CLOSE 1
|
||||
#define NGX_HTTP_CONNECTION_KEEP_ALIVE 2
|
||||
|
||||
|
||||
#define NGX_NONE 1
|
||||
|
||||
|
||||
#define NGX_HTTP_PARSE_HEADER_DONE 1
|
||||
|
||||
#define NGX_HTTP_CLIENT_ERROR 10
|
||||
#define NGX_HTTP_PARSE_INVALID_METHOD 10
|
||||
#define NGX_HTTP_PARSE_INVALID_REQUEST 11
|
||||
#define NGX_HTTP_PARSE_INVALID_09_METHOD 12
|
||||
|
||||
#define NGX_HTTP_PARSE_INVALID_HEADER 13
|
||||
|
||||
|
||||
/* unused 1 */
|
||||
#define NGX_HTTP_SUBREQUEST_IN_MEMORY 2
|
||||
#define NGX_HTTP_SUBREQUEST_WAITED 4
|
||||
#define NGX_HTTP_LOG_UNSAFE 8
|
||||
|
||||
|
||||
#define NGX_HTTP_CONTINUE 100
|
||||
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
|
||||
#define NGX_HTTP_PROCESSING 102
|
||||
|
||||
#define NGX_HTTP_OK 200
|
||||
#define NGX_HTTP_CREATED 201
|
||||
#define NGX_HTTP_ACCEPTED 202
|
||||
#define NGX_HTTP_NO_CONTENT 204
|
||||
#define NGX_HTTP_PARTIAL_CONTENT 206
|
||||
|
||||
#define NGX_HTTP_SPECIAL_RESPONSE 300
|
||||
#define NGX_HTTP_MOVED_PERMANENTLY 301
|
||||
#define NGX_HTTP_MOVED_TEMPORARILY 302
|
||||
#define NGX_HTTP_SEE_OTHER 303
|
||||
#define NGX_HTTP_NOT_MODIFIED 304
|
||||
#define NGX_HTTP_TEMPORARY_REDIRECT 307
|
||||
|
||||
#define NGX_HTTP_BAD_REQUEST 400
|
||||
#define NGX_HTTP_UNAUTHORIZED 401
|
||||
#define NGX_HTTP_FORBIDDEN 403
|
||||
#define NGX_HTTP_NOT_FOUND 404
|
||||
#define NGX_HTTP_NOT_ALLOWED 405
|
||||
#define NGX_HTTP_REQUEST_TIME_OUT 408
|
||||
#define NGX_HTTP_CONFLICT 409
|
||||
#define NGX_HTTP_LENGTH_REQUIRED 411
|
||||
#define NGX_HTTP_PRECONDITION_FAILED 412
|
||||
#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413
|
||||
#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414
|
||||
#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415
|
||||
#define NGX_HTTP_RANGE_NOT_SATISFIABLE 416
|
||||
|
||||
|
||||
/* Our own HTTP codes */
|
||||
|
||||
/* The special code to close connection without any response */
|
||||
#define NGX_HTTP_CLOSE 444
|
||||
|
||||
#define NGX_HTTP_NGINX_CODES 494
|
||||
|
||||
#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494
|
||||
|
||||
#define NGX_HTTPS_CERT_ERROR 495
|
||||
#define NGX_HTTPS_NO_CERT 496
|
||||
|
||||
/*
|
||||
* We use the special code for the plain HTTP requests that are sent to
|
||||
* HTTPS port to distinguish it from 4XX in an error page redirection
|
||||
*/
|
||||
#define NGX_HTTP_TO_HTTPS 497
|
||||
|
||||
/* 498 is the canceled code for the requests with invalid host name */
|
||||
|
||||
/*
|
||||
* HTTP does not define the code for the case when a client closed
|
||||
* the connection while we are processing its request so we introduce
|
||||
* own code to log such situation when a client has closed the connection
|
||||
* before we even try to send the HTTP header to it
|
||||
*/
|
||||
#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499
|
||||
|
||||
|
||||
#define NGX_HTTP_INTERNAL_SERVER_ERROR 500
|
||||
#define NGX_HTTP_NOT_IMPLEMENTED 501
|
||||
#define NGX_HTTP_BAD_GATEWAY 502
|
||||
#define NGX_HTTP_SERVICE_UNAVAILABLE 503
|
||||
#define NGX_HTTP_GATEWAY_TIME_OUT 504
|
||||
#define NGX_HTTP_INSUFFICIENT_STORAGE 507
|
||||
|
||||
|
||||
#define NGX_HTTP_LOWLEVEL_BUFFERED 0xf0
|
||||
#define NGX_HTTP_WRITE_BUFFERED 0x10
|
||||
#define NGX_HTTP_GZIP_BUFFERED 0x20
|
||||
#define NGX_HTTP_SSI_BUFFERED 0x01
|
||||
#define NGX_HTTP_SUB_BUFFERED 0x02
|
||||
#define NGX_HTTP_COPY_BUFFERED 0x04
|
||||
|
||||
|
||||
typedef enum {
|
||||
NGX_HTTP_INITING_REQUEST_STATE = 0,
|
||||
NGX_HTTP_READING_REQUEST_STATE,
|
||||
NGX_HTTP_PROCESS_REQUEST_STATE,
|
||||
|
||||
NGX_HTTP_CONNECT_UPSTREAM_STATE,
|
||||
NGX_HTTP_WRITING_UPSTREAM_STATE,
|
||||
NGX_HTTP_READING_UPSTREAM_STATE,
|
||||
|
||||
NGX_HTTP_WRITING_REQUEST_STATE,
|
||||
NGX_HTTP_LINGERING_CLOSE_STATE,
|
||||
NGX_HTTP_KEEPALIVE_STATE
|
||||
} ngx_http_state_e;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_uint_t offset;
|
||||
ngx_http_header_handler_pt handler;
|
||||
} ngx_http_header_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_uint_t offset;
|
||||
} ngx_http_header_out_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_list_t headers;
|
||||
|
||||
ngx_table_elt_t *host;
|
||||
ngx_table_elt_t *connection;
|
||||
ngx_table_elt_t *if_modified_since;
|
||||
ngx_table_elt_t *if_unmodified_since;
|
||||
ngx_table_elt_t *if_match;
|
||||
ngx_table_elt_t *if_none_match;
|
||||
ngx_table_elt_t *user_agent;
|
||||
ngx_table_elt_t *referer;
|
||||
ngx_table_elt_t *content_length;
|
||||
ngx_table_elt_t *content_type;
|
||||
|
||||
ngx_table_elt_t *range;
|
||||
ngx_table_elt_t *if_range;
|
||||
|
||||
ngx_table_elt_t *transfer_encoding;
|
||||
ngx_table_elt_t *expect;
|
||||
ngx_table_elt_t *upgrade;
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
ngx_table_elt_t *accept_encoding;
|
||||
ngx_table_elt_t *via;
|
||||
#endif
|
||||
|
||||
ngx_table_elt_t *authorization;
|
||||
|
||||
ngx_table_elt_t *keep_alive;
|
||||
|
||||
#if (NGX_HTTP_X_FORWARDED_FOR)
|
||||
ngx_array_t x_forwarded_for;
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_REALIP)
|
||||
ngx_table_elt_t *x_real_ip;
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_HEADERS)
|
||||
ngx_table_elt_t *accept;
|
||||
ngx_table_elt_t *accept_language;
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_DAV)
|
||||
ngx_table_elt_t *depth;
|
||||
ngx_table_elt_t *destination;
|
||||
ngx_table_elt_t *overwrite;
|
||||
ngx_table_elt_t *date;
|
||||
#endif
|
||||
|
||||
ngx_str_t user;
|
||||
ngx_str_t passwd;
|
||||
|
||||
ngx_array_t cookies;
|
||||
|
||||
ngx_str_t server;
|
||||
off_t content_length_n;
|
||||
time_t keep_alive_n;
|
||||
|
||||
unsigned connection_type:2;
|
||||
unsigned chunked:1;
|
||||
unsigned msie:1;
|
||||
unsigned msie6:1;
|
||||
unsigned opera:1;
|
||||
unsigned gecko:1;
|
||||
unsigned chrome:1;
|
||||
unsigned safari:1;
|
||||
unsigned konqueror:1;
|
||||
} ngx_http_headers_in_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_list_t headers;
|
||||
|
||||
ngx_uint_t status;
|
||||
ngx_str_t status_line;
|
||||
|
||||
ngx_table_elt_t *server;
|
||||
ngx_table_elt_t *date;
|
||||
ngx_table_elt_t *content_length;
|
||||
ngx_table_elt_t *content_encoding;
|
||||
ngx_table_elt_t *location;
|
||||
ngx_table_elt_t *refresh;
|
||||
ngx_table_elt_t *last_modified;
|
||||
ngx_table_elt_t *content_range;
|
||||
ngx_table_elt_t *accept_ranges;
|
||||
ngx_table_elt_t *www_authenticate;
|
||||
ngx_table_elt_t *expires;
|
||||
ngx_table_elt_t *etag;
|
||||
|
||||
ngx_str_t *override_charset;
|
||||
|
||||
size_t content_type_len;
|
||||
ngx_str_t content_type;
|
||||
ngx_str_t charset;
|
||||
u_char *content_type_lowcase;
|
||||
ngx_uint_t content_type_hash;
|
||||
|
||||
ngx_array_t cache_control;
|
||||
|
||||
off_t content_length_n;
|
||||
time_t date_time;
|
||||
time_t last_modified_time;
|
||||
} ngx_http_headers_out_t;
|
||||
|
||||
|
||||
typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
|
||||
|
||||
typedef struct {
|
||||
ngx_temp_file_t *temp_file;
|
||||
ngx_chain_t *bufs;
|
||||
ngx_buf_t *buf;
|
||||
off_t rest;
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
ngx_http_chunked_t *chunked;
|
||||
ngx_http_client_body_handler_pt post_handler;
|
||||
} ngx_http_request_body_t;
|
||||
|
||||
|
||||
typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t;
|
||||
|
||||
typedef struct {
|
||||
ngx_http_addr_conf_t *addr_conf;
|
||||
ngx_http_conf_ctx_t *conf_ctx;
|
||||
|
||||
#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
|
||||
ngx_str_t *ssl_servername;
|
||||
#if (NGX_PCRE)
|
||||
ngx_http_regex_t *ssl_servername_regex;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ngx_buf_t **busy;
|
||||
ngx_int_t nbusy;
|
||||
|
||||
ngx_buf_t **free;
|
||||
ngx_int_t nfree;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
unsigned ssl:1;
|
||||
#endif
|
||||
unsigned proxy_protocol:1;
|
||||
} ngx_http_connection_t;
|
||||
|
||||
|
||||
typedef void (*ngx_http_cleanup_pt)(void *data);
|
||||
|
||||
typedef struct ngx_http_cleanup_s ngx_http_cleanup_t;
|
||||
|
||||
struct ngx_http_cleanup_s {
|
||||
ngx_http_cleanup_pt handler;
|
||||
void *data;
|
||||
ngx_http_cleanup_t *next;
|
||||
};
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,
|
||||
void *data, ngx_int_t rc);
|
||||
|
||||
typedef struct {
|
||||
ngx_http_post_subrequest_pt handler;
|
||||
void *data;
|
||||
} ngx_http_post_subrequest_t;
|
||||
|
||||
|
||||
typedef struct ngx_http_postponed_request_s ngx_http_postponed_request_t;
|
||||
|
||||
struct ngx_http_postponed_request_s {
|
||||
ngx_http_request_t *request;
|
||||
ngx_chain_t *out;
|
||||
ngx_http_postponed_request_t *next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ngx_http_posted_request_s ngx_http_posted_request_t;
|
||||
|
||||
struct ngx_http_posted_request_s {
|
||||
ngx_http_request_t *request;
|
||||
ngx_http_posted_request_t *next;
|
||||
};
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
|
||||
typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
|
||||
|
||||
|
||||
struct ngx_http_request_s {
|
||||
uint32_t signature; /* "HTTP" */
|
||||
|
||||
ngx_connection_t *connection;
|
||||
|
||||
void **ctx;
|
||||
void **main_conf;
|
||||
void **srv_conf;
|
||||
void **loc_conf;
|
||||
|
||||
ngx_http_event_handler_pt read_event_handler;
|
||||
ngx_http_event_handler_pt write_event_handler;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_cache_t *cache;
|
||||
#endif
|
||||
|
||||
ngx_http_upstream_t *upstream;
|
||||
ngx_array_t *upstream_states;
|
||||
/* of ngx_http_upstream_state_t */
|
||||
|
||||
ngx_pool_t *pool;
|
||||
ngx_buf_t *header_in;
|
||||
|
||||
ngx_http_headers_in_t headers_in;
|
||||
ngx_http_headers_out_t headers_out;
|
||||
|
||||
ngx_http_request_body_t *request_body;
|
||||
|
||||
time_t lingering_time;
|
||||
time_t start_sec;
|
||||
ngx_msec_t start_msec;
|
||||
|
||||
ngx_uint_t method;
|
||||
ngx_uint_t http_version;
|
||||
|
||||
ngx_str_t request_line;
|
||||
ngx_str_t uri;
|
||||
ngx_str_t args;
|
||||
ngx_str_t exten;
|
||||
ngx_str_t unparsed_uri;
|
||||
|
||||
ngx_str_t method_name;
|
||||
ngx_str_t http_protocol;
|
||||
|
||||
ngx_chain_t *out;
|
||||
ngx_http_request_t *main;
|
||||
ngx_http_request_t *parent;
|
||||
ngx_http_postponed_request_t *postponed;
|
||||
ngx_http_post_subrequest_t *post_subrequest;
|
||||
ngx_http_posted_request_t *posted_requests;
|
||||
|
||||
ngx_int_t phase_handler;
|
||||
ngx_http_handler_pt content_handler;
|
||||
ngx_uint_t access_code;
|
||||
|
||||
ngx_http_variable_value_t *variables;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_uint_t ncaptures;
|
||||
int *captures;
|
||||
u_char *captures_data;
|
||||
#endif
|
||||
|
||||
size_t limit_rate;
|
||||
size_t limit_rate_after;
|
||||
|
||||
/* used to learn the Apache compatible response length without a header */
|
||||
size_t header_size;
|
||||
|
||||
off_t request_length;
|
||||
|
||||
ngx_uint_t err_status;
|
||||
|
||||
ngx_http_connection_t *http_connection;
|
||||
#if (NGX_HTTP_SPDY)
|
||||
ngx_http_spdy_stream_t *spdy_stream;
|
||||
#endif
|
||||
|
||||
ngx_http_log_handler_pt log_handler;
|
||||
|
||||
ngx_http_cleanup_t *cleanup;
|
||||
|
||||
unsigned subrequests:8;
|
||||
unsigned count:8;
|
||||
unsigned blocked:8;
|
||||
|
||||
unsigned aio:1;
|
||||
|
||||
unsigned http_state:4;
|
||||
|
||||
/* URI with "/." and on Win32 with "//" */
|
||||
unsigned complex_uri:1;
|
||||
|
||||
/* URI with "%" */
|
||||
unsigned quoted_uri:1;
|
||||
|
||||
/* URI with "+" */
|
||||
unsigned plus_in_uri:1;
|
||||
|
||||
/* URI with " " */
|
||||
unsigned space_in_uri:1;
|
||||
|
||||
unsigned invalid_header:1;
|
||||
|
||||
unsigned add_uri_to_alias:1;
|
||||
unsigned valid_location:1;
|
||||
unsigned valid_unparsed_uri:1;
|
||||
unsigned uri_changed:1;
|
||||
unsigned uri_changes:4;
|
||||
|
||||
unsigned request_body_in_single_buf:1;
|
||||
unsigned request_body_in_file_only:1;
|
||||
unsigned request_body_in_persistent_file:1;
|
||||
unsigned request_body_in_clean_file:1;
|
||||
unsigned request_body_file_group_access:1;
|
||||
unsigned request_body_file_log_level:3;
|
||||
unsigned request_body_no_buffering:1;
|
||||
|
||||
unsigned subrequest_in_memory:1;
|
||||
unsigned waited:1;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
unsigned cached:1;
|
||||
#endif
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
unsigned gzip_tested:1;
|
||||
unsigned gzip_ok:1;
|
||||
unsigned gzip_vary:1;
|
||||
#endif
|
||||
|
||||
unsigned proxy:1;
|
||||
unsigned bypass_cache:1;
|
||||
unsigned no_cache:1;
|
||||
|
||||
/*
|
||||
* instead of using the request context data in
|
||||
* ngx_http_limit_conn_module and ngx_http_limit_req_module
|
||||
* we use the single bits in the request structure
|
||||
*/
|
||||
unsigned limit_conn_set:1;
|
||||
unsigned limit_req_set:1;
|
||||
|
||||
#if 0
|
||||
unsigned cacheable:1;
|
||||
#endif
|
||||
|
||||
unsigned pipeline:1;
|
||||
unsigned chunked:1;
|
||||
unsigned header_only:1;
|
||||
unsigned keepalive:1;
|
||||
unsigned lingering_close:1;
|
||||
unsigned discard_body:1;
|
||||
unsigned reading_body:1;
|
||||
unsigned internal:1;
|
||||
unsigned error_page:1;
|
||||
unsigned filter_finalize:1;
|
||||
unsigned post_action:1;
|
||||
unsigned request_complete:1;
|
||||
unsigned request_output:1;
|
||||
unsigned header_sent:1;
|
||||
unsigned expect_tested:1;
|
||||
unsigned root_tested:1;
|
||||
unsigned done:1;
|
||||
unsigned logged:1;
|
||||
|
||||
unsigned buffered:4;
|
||||
|
||||
unsigned main_filter_need_in_memory:1;
|
||||
unsigned filter_need_in_memory:1;
|
||||
unsigned filter_need_temporary:1;
|
||||
unsigned allow_ranges:1;
|
||||
unsigned single_range:1;
|
||||
unsigned disable_not_modified:1;
|
||||
|
||||
#if (NGX_STAT_STUB)
|
||||
unsigned stat_reading:1;
|
||||
unsigned stat_writing:1;
|
||||
#endif
|
||||
|
||||
/* used to parse HTTP headers */
|
||||
|
||||
ngx_uint_t state;
|
||||
|
||||
ngx_uint_t header_hash;
|
||||
ngx_uint_t lowcase_index;
|
||||
u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
|
||||
|
||||
u_char *header_name_start;
|
||||
u_char *header_name_end;
|
||||
u_char *header_start;
|
||||
u_char *header_end;
|
||||
|
||||
/*
|
||||
* a memory that can be reused after parsing a request line
|
||||
* via ngx_http_ephemeral_t
|
||||
*/
|
||||
|
||||
u_char *uri_start;
|
||||
u_char *uri_end;
|
||||
u_char *uri_ext;
|
||||
u_char *args_start;
|
||||
u_char *request_start;
|
||||
u_char *request_end;
|
||||
u_char *method_end;
|
||||
u_char *schema_start;
|
||||
u_char *schema_end;
|
||||
u_char *host_start;
|
||||
u_char *host_end;
|
||||
u_char *port_start;
|
||||
u_char *port_end;
|
||||
|
||||
unsigned http_minor:16;
|
||||
unsigned http_major:16;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_posted_request_t terminal_posted_request;
|
||||
} ngx_http_ephemeral_t;
|
||||
|
||||
|
||||
#define ngx_http_ephemeral(r) (void *) (&r->uri_start)
|
||||
|
||||
|
||||
extern ngx_http_header_t ngx_http_headers_in[];
|
||||
extern ngx_http_header_out_t ngx_http_headers_out[];
|
||||
|
||||
|
||||
#define ngx_http_set_connection_log(c, l) \
|
||||
\
|
||||
c->log->file = l->file; \
|
||||
c->log->next = l->next; \
|
||||
c->log->writer = l->writer; \
|
||||
c->log->wdata = l->wdata; \
|
||||
if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \
|
||||
c->log->log_level = l->log_level; \
|
||||
}
|
||||
|
||||
|
||||
#define ngx_http_set_log_request(log, r) \
|
||||
((ngx_http_log_ctx_t *) log->data)->current_request = r
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,257 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_
|
||||
#define _NGX_HTTP_SCRIPT_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char *ip;
|
||||
u_char *pos;
|
||||
ngx_http_variable_value_t *sp;
|
||||
|
||||
ngx_str_t buf;
|
||||
ngx_str_t line;
|
||||
|
||||
/* the start of the rewritten arguments */
|
||||
u_char *args;
|
||||
|
||||
unsigned flushed:1;
|
||||
unsigned skip:1;
|
||||
unsigned quote:1;
|
||||
unsigned is_args:1;
|
||||
unsigned log:1;
|
||||
|
||||
ngx_int_t status;
|
||||
ngx_http_request_t *request;
|
||||
} ngx_http_script_engine_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_conf_t *cf;
|
||||
ngx_str_t *source;
|
||||
|
||||
ngx_array_t **flushes;
|
||||
ngx_array_t **lengths;
|
||||
ngx_array_t **values;
|
||||
|
||||
ngx_uint_t variables;
|
||||
ngx_uint_t ncaptures;
|
||||
ngx_uint_t captures_mask;
|
||||
ngx_uint_t size;
|
||||
|
||||
void *main;
|
||||
|
||||
unsigned compile_args:1;
|
||||
unsigned complete_lengths:1;
|
||||
unsigned complete_values:1;
|
||||
unsigned zero:1;
|
||||
unsigned conf_prefix:1;
|
||||
unsigned root_prefix:1;
|
||||
|
||||
unsigned dup_capture:1;
|
||||
unsigned args:1;
|
||||
} ngx_http_script_compile_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t value;
|
||||
ngx_uint_t *flushes;
|
||||
void *lengths;
|
||||
void *values;
|
||||
} ngx_http_complex_value_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_conf_t *cf;
|
||||
ngx_str_t *value;
|
||||
ngx_http_complex_value_t *complex_value;
|
||||
|
||||
unsigned zero:1;
|
||||
unsigned conf_prefix:1;
|
||||
unsigned root_prefix:1;
|
||||
} ngx_http_compile_complex_value_t;
|
||||
|
||||
|
||||
typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);
|
||||
typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t len;
|
||||
} ngx_http_script_copy_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t index;
|
||||
} ngx_http_script_var_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
ngx_http_set_variable_pt handler;
|
||||
uintptr_t data;
|
||||
} ngx_http_script_var_handler_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t n;
|
||||
} ngx_http_script_copy_capture_code_t;
|
||||
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
ngx_http_regex_t *regex;
|
||||
ngx_array_t *lengths;
|
||||
uintptr_t size;
|
||||
uintptr_t status;
|
||||
uintptr_t next;
|
||||
|
||||
uintptr_t test:1;
|
||||
uintptr_t negative_test:1;
|
||||
uintptr_t uri:1;
|
||||
uintptr_t args:1;
|
||||
|
||||
/* add the r->args to the new arguments */
|
||||
uintptr_t add_args:1;
|
||||
|
||||
uintptr_t redirect:1;
|
||||
uintptr_t break_cycle:1;
|
||||
|
||||
ngx_str_t name;
|
||||
} ngx_http_script_regex_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
|
||||
uintptr_t uri:1;
|
||||
uintptr_t args:1;
|
||||
|
||||
/* add the r->args to the new arguments */
|
||||
uintptr_t add_args:1;
|
||||
|
||||
uintptr_t redirect:1;
|
||||
} ngx_http_script_regex_end_code_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t conf_prefix;
|
||||
} ngx_http_script_full_name_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t status;
|
||||
ngx_http_complex_value_t text;
|
||||
} ngx_http_script_return_code_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
ngx_http_script_file_plain = 0,
|
||||
ngx_http_script_file_not_plain,
|
||||
ngx_http_script_file_dir,
|
||||
ngx_http_script_file_not_dir,
|
||||
ngx_http_script_file_exists,
|
||||
ngx_http_script_file_not_exists,
|
||||
ngx_http_script_file_exec,
|
||||
ngx_http_script_file_not_exec
|
||||
} ngx_http_script_file_op_e;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t op;
|
||||
} ngx_http_script_file_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t next;
|
||||
void **loc_conf;
|
||||
} ngx_http_script_if_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
ngx_array_t *lengths;
|
||||
} ngx_http_script_complex_value_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_script_code_pt code;
|
||||
uintptr_t value;
|
||||
uintptr_t text_len;
|
||||
uintptr_t text_data;
|
||||
} ngx_http_script_value_code_t;
|
||||
|
||||
|
||||
void ngx_http_script_flush_complex_value(ngx_http_request_t *r,
|
||||
ngx_http_complex_value_t *val);
|
||||
ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
|
||||
ngx_http_complex_value_t *val, ngx_str_t *value);
|
||||
ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);
|
||||
char *ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,
|
||||
ngx_array_t *predicates);
|
||||
char *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);
|
||||
ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);
|
||||
u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
|
||||
void *code_lengths, size_t reserved, void *code_values);
|
||||
void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
|
||||
ngx_array_t *indices);
|
||||
|
||||
void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,
|
||||
size_t size);
|
||||
void *ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code);
|
||||
|
||||
size_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_copy_code(ngx_http_script_engine_t *e);
|
||||
size_t ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);
|
||||
size_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);
|
||||
size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_start_args_code(ngx_http_script_engine_t *e);
|
||||
#if (NGX_PCRE)
|
||||
void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);
|
||||
#endif
|
||||
void ngx_http_script_return_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_break_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_if_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_equal_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_not_equal_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_file_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_value_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_set_var_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_var_code(ngx_http_script_engine_t *e);
|
||||
void ngx_http_script_nop_code(ngx_http_script_engine_t *e);
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) Nginx, Inc.
|
||||
* Copyright (C) Valentin V. Bartenev
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_SPDY_H_INCLUDED_
|
||||
#define _NGX_HTTP_SPDY_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
#define NGX_SPDY_VERSION 3
|
||||
|
||||
#define NGX_SPDY_NPN_ADVERTISE "\x08spdy/3.1"
|
||||
#define NGX_SPDY_NPN_NEGOTIATED "spdy/3.1"
|
||||
|
||||
#define NGX_SPDY_STATE_BUFFER_SIZE 16
|
||||
|
||||
#define NGX_SPDY_CTL_BIT 1
|
||||
|
||||
#define NGX_SPDY_SYN_STREAM 1
|
||||
#define NGX_SPDY_SYN_REPLY 2
|
||||
#define NGX_SPDY_RST_STREAM 3
|
||||
#define NGX_SPDY_SETTINGS 4
|
||||
#define NGX_SPDY_PING 6
|
||||
#define NGX_SPDY_GOAWAY 7
|
||||
#define NGX_SPDY_HEADERS 8
|
||||
#define NGX_SPDY_WINDOW_UPDATE 9
|
||||
|
||||
#define NGX_SPDY_FRAME_HEADER_SIZE 8
|
||||
|
||||
#define NGX_SPDY_SID_SIZE 4
|
||||
#define NGX_SPDY_DELTA_SIZE 4
|
||||
|
||||
#define NGX_SPDY_SYN_STREAM_SIZE 10
|
||||
#define NGX_SPDY_SYN_REPLY_SIZE 4
|
||||
#define NGX_SPDY_RST_STREAM_SIZE 8
|
||||
#define NGX_SPDY_PING_SIZE 4
|
||||
#define NGX_SPDY_GOAWAY_SIZE 8
|
||||
#define NGX_SPDY_WINDOW_UPDATE_SIZE 8
|
||||
#define NGX_SPDY_NV_NUM_SIZE 4
|
||||
#define NGX_SPDY_NV_NLEN_SIZE 4
|
||||
#define NGX_SPDY_NV_VLEN_SIZE 4
|
||||
#define NGX_SPDY_SETTINGS_NUM_SIZE 4
|
||||
#define NGX_SPDY_SETTINGS_FID_SIZE 4
|
||||
#define NGX_SPDY_SETTINGS_VAL_SIZE 4
|
||||
|
||||
#define NGX_SPDY_SETTINGS_PAIR_SIZE \
|
||||
(NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)
|
||||
|
||||
#define NGX_SPDY_HIGHEST_PRIORITY 0
|
||||
#define NGX_SPDY_LOWEST_PRIORITY 7
|
||||
|
||||
#define NGX_SPDY_FLAG_FIN 0x01
|
||||
#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02
|
||||
#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01
|
||||
|
||||
#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1)
|
||||
|
||||
#define NGX_SPDY_DATA_DISCARD 1
|
||||
#define NGX_SPDY_DATA_ERROR 2
|
||||
#define NGX_SPDY_DATA_INTERNAL_ERROR 3
|
||||
|
||||
|
||||
typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t;
|
||||
typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t;
|
||||
|
||||
|
||||
typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,
|
||||
u_char *pos, u_char *end);
|
||||
|
||||
struct ngx_http_spdy_connection_s {
|
||||
ngx_connection_t *connection;
|
||||
ngx_http_connection_t *http_connection;
|
||||
|
||||
ngx_uint_t processing;
|
||||
|
||||
size_t send_window;
|
||||
size_t recv_window;
|
||||
size_t init_window;
|
||||
|
||||
ngx_queue_t waiting;
|
||||
|
||||
u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE];
|
||||
size_t buffer_used;
|
||||
ngx_http_spdy_handler_pt handler;
|
||||
|
||||
z_stream zstream_in;
|
||||
z_stream zstream_out;
|
||||
|
||||
ngx_pool_t *pool;
|
||||
|
||||
ngx_http_spdy_out_frame_t *free_ctl_frames;
|
||||
ngx_connection_t *free_fake_connections;
|
||||
|
||||
ngx_http_spdy_stream_t **streams_index;
|
||||
|
||||
ngx_http_spdy_out_frame_t *last_out;
|
||||
|
||||
ngx_queue_t posted;
|
||||
|
||||
ngx_http_spdy_stream_t *stream;
|
||||
|
||||
ngx_uint_t entries;
|
||||
size_t length;
|
||||
u_char flags;
|
||||
|
||||
ngx_uint_t last_sid;
|
||||
|
||||
unsigned blocked:1;
|
||||
unsigned incomplete:1;
|
||||
};
|
||||
|
||||
|
||||
struct ngx_http_spdy_stream_s {
|
||||
ngx_uint_t id;
|
||||
ngx_http_request_t *request;
|
||||
ngx_http_spdy_connection_t *connection;
|
||||
ngx_http_spdy_stream_t *index;
|
||||
|
||||
ngx_uint_t header_buffers;
|
||||
ngx_uint_t queued;
|
||||
|
||||
/*
|
||||
* A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the
|
||||
* send_window to become negative, hence it's signed.
|
||||
*/
|
||||
ssize_t send_window;
|
||||
size_t recv_window;
|
||||
|
||||
ngx_http_spdy_out_frame_t *free_frames;
|
||||
ngx_chain_t *free_data_headers;
|
||||
ngx_chain_t *free_bufs;
|
||||
|
||||
ngx_queue_t queue;
|
||||
|
||||
unsigned priority:3;
|
||||
unsigned handled:1;
|
||||
unsigned blocked:1;
|
||||
unsigned exhausted:1;
|
||||
unsigned in_closed:1;
|
||||
unsigned out_closed:1;
|
||||
unsigned skip_data:2;
|
||||
};
|
||||
|
||||
|
||||
struct ngx_http_spdy_out_frame_s {
|
||||
ngx_http_spdy_out_frame_t *next;
|
||||
ngx_chain_t *first;
|
||||
ngx_chain_t *last;
|
||||
ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc,
|
||||
ngx_http_spdy_out_frame_t *frame);
|
||||
|
||||
ngx_http_spdy_stream_t *stream;
|
||||
size_t length;
|
||||
|
||||
ngx_uint_t priority;
|
||||
unsigned blocked:1;
|
||||
unsigned fin:1;
|
||||
};
|
||||
|
||||
|
||||
static ngx_inline void
|
||||
ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,
|
||||
ngx_http_spdy_out_frame_t *frame)
|
||||
{
|
||||
ngx_http_spdy_out_frame_t **out;
|
||||
|
||||
for (out = &sc->last_out; *out; out = &(*out)->next)
|
||||
{
|
||||
/*
|
||||
* NB: higher values represent lower priorities.
|
||||
*/
|
||||
if (frame->priority >= (*out)->priority) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
frame->next = *out;
|
||||
*out = frame;
|
||||
}
|
||||
|
||||
|
||||
static ngx_inline void
|
||||
ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,
|
||||
ngx_http_spdy_out_frame_t *frame)
|
||||
{
|
||||
ngx_http_spdy_out_frame_t **out;
|
||||
|
||||
for (out = &sc->last_out; *out; out = &(*out)->next)
|
||||
{
|
||||
if ((*out)->blocked) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
frame->next = *out;
|
||||
*out = frame;
|
||||
}
|
||||
|
||||
|
||||
void ngx_http_spdy_init(ngx_event_t *rev);
|
||||
void ngx_http_spdy_request_headers_init(void);
|
||||
|
||||
ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,
|
||||
ngx_http_client_body_handler_pt post_handler);
|
||||
|
||||
void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);
|
||||
|
||||
ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);
|
||||
|
||||
|
||||
#define ngx_spdy_frame_aligned_write_uint16(p, s) \
|
||||
(*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
|
||||
|
||||
#define ngx_spdy_frame_aligned_write_uint32(p, s) \
|
||||
(*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
|
||||
|
||||
#if (NGX_HAVE_NONALIGNED)
|
||||
|
||||
#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16
|
||||
#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32
|
||||
|
||||
#else
|
||||
|
||||
#define ngx_spdy_frame_write_uint16(p, s) \
|
||||
((p)[0] = (u_char) ((s) >> 8), \
|
||||
(p)[1] = (u_char) (s), \
|
||||
(p) + sizeof(uint16_t))
|
||||
|
||||
#define ngx_spdy_frame_write_uint32(p, s) \
|
||||
((p)[0] = (u_char) ((s) >> 24), \
|
||||
(p)[1] = (u_char) ((s) >> 16), \
|
||||
(p)[2] = (u_char) ((s) >> 8), \
|
||||
(p)[3] = (u_char) (s), \
|
||||
(p) + sizeof(uint32_t))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define ngx_spdy_ctl_frame_head(t) \
|
||||
((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))
|
||||
|
||||
#define ngx_spdy_frame_write_head(p, t) \
|
||||
ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))
|
||||
|
||||
#define ngx_spdy_frame_write_flags_and_len(p, f, l) \
|
||||
ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))
|
||||
#define ngx_spdy_frame_write_flags_and_id(p, f, i) \
|
||||
ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i))
|
||||
|
||||
#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32
|
||||
#define ngx_spdy_frame_write_window ngx_spdy_frame_aligned_write_uint32
|
||||
|
||||
#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,408 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Nginx, Inc.
|
||||
* Copyright (C) Valentin V. Bartenev
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_http_spdy_module.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);
|
||||
|
||||
static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
|
||||
static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);
|
||||
|
||||
static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);
|
||||
static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
|
||||
static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,
|
||||
void *data);
|
||||
static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);
|
||||
static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,
|
||||
void *data);
|
||||
static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data);
|
||||
|
||||
|
||||
static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = {
|
||||
ngx_conf_check_num_bounds, 0, 9
|
||||
};
|
||||
|
||||
static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post =
|
||||
{ ngx_http_spdy_recv_buffer_size };
|
||||
static ngx_conf_post_t ngx_http_spdy_pool_size_post =
|
||||
{ ngx_http_spdy_pool_size };
|
||||
static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post =
|
||||
{ ngx_http_spdy_streams_index_mask };
|
||||
static ngx_conf_post_t ngx_http_spdy_chunk_size_post =
|
||||
{ ngx_http_spdy_chunk_size };
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_spdy_commands[] = {
|
||||
|
||||
{ ngx_string("spdy_recv_buffer_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),
|
||||
&ngx_http_spdy_recv_buffer_size_post },
|
||||
|
||||
{ ngx_string("spdy_pool_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, pool_size),
|
||||
&ngx_http_spdy_pool_size_post },
|
||||
|
||||
{ ngx_string("spdy_max_concurrent_streams"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("spdy_streams_index_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),
|
||||
&ngx_http_spdy_streams_index_mask_post },
|
||||
|
||||
{ ngx_string("spdy_recv_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("spdy_keepalive_timeout"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("spdy_headers_comp"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_srv_conf_t, headers_comp),
|
||||
&ngx_http_spdy_headers_comp_bounds },
|
||||
|
||||
{ ngx_string("spdy_chunk_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_spdy_loc_conf_t, chunk_size),
|
||||
&ngx_http_spdy_chunk_size_post },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_spdy_module_ctx = {
|
||||
ngx_http_spdy_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_http_spdy_create_main_conf, /* create main configuration */
|
||||
ngx_http_spdy_init_main_conf, /* init main configuration */
|
||||
|
||||
ngx_http_spdy_create_srv_conf, /* create server configuration */
|
||||
ngx_http_spdy_merge_srv_conf, /* merge server configuration */
|
||||
|
||||
ngx_http_spdy_create_loc_conf, /* create location configuration */
|
||||
ngx_http_spdy_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_spdy_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_spdy_module_ctx, /* module context */
|
||||
ngx_http_spdy_commands, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
ngx_http_spdy_module_init, /* init module */
|
||||
NULL, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_variable_t ngx_http_spdy_vars[] = {
|
||||
|
||||
{ ngx_string("spdy"), NULL,
|
||||
ngx_http_spdy_variable, 0, 0, 0 },
|
||||
|
||||
{ ngx_string("spdy_request_priority"), NULL,
|
||||
ngx_http_spdy_request_priority_variable, 0, 0, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_spdy_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_http_spdy_vars; v->name.len; v++) {
|
||||
var = ngx_http_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_spdy_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
if (r->spdy_stream) {
|
||||
v->len = sizeof("3.1") - 1;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = (u_char *) "3.1";
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
*v = ngx_http_variable_null_value;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
if (r->spdy_stream) {
|
||||
v->len = 1;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
v->data = ngx_pnalloc(r->pool, 1);
|
||||
if (v->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->data[0] = '0' + (u_char) r->spdy_stream->priority;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
*v = ngx_http_variable_null_value;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_spdy_module_init(ngx_cycle_t *cycle)
|
||||
{
|
||||
ngx_http_spdy_request_headers_init();
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_spdy_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_spdy_main_conf_t *smcf;
|
||||
|
||||
smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));
|
||||
if (smcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
return smcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)
|
||||
{
|
||||
ngx_http_spdy_main_conf_t *smcf = conf;
|
||||
|
||||
ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_spdy_srv_conf_t *sscf;
|
||||
|
||||
sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));
|
||||
if (sscf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sscf->pool_size = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
sscf->concurrent_streams = NGX_CONF_UNSET_UINT;
|
||||
sscf->streams_index_mask = NGX_CONF_UNSET_UINT;
|
||||
|
||||
sscf->recv_timeout = NGX_CONF_UNSET_MSEC;
|
||||
sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
|
||||
|
||||
sscf->headers_comp = NGX_CONF_UNSET;
|
||||
|
||||
return sscf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_spdy_srv_conf_t *prev = parent;
|
||||
ngx_http_spdy_srv_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
|
||||
|
||||
ngx_conf_merge_uint_value(conf->concurrent_streams,
|
||||
prev->concurrent_streams, 100);
|
||||
|
||||
ngx_conf_merge_uint_value(conf->streams_index_mask,
|
||||
prev->streams_index_mask, 32 - 1);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->recv_timeout,
|
||||
prev->recv_timeout, 30000);
|
||||
ngx_conf_merge_msec_value(conf->keepalive_timeout,
|
||||
prev->keepalive_timeout, 180000);
|
||||
|
||||
ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_spdy_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_spdy_loc_conf_t *slcf;
|
||||
|
||||
slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t));
|
||||
if (slcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slcf->chunk_size = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
return slcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_spdy_loc_conf_t *prev = parent;
|
||||
ngx_http_spdy_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
size_t *sp = data;
|
||||
|
||||
if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {
|
||||
return "value is too small";
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
size_t *sp = data;
|
||||
|
||||
if (*sp < NGX_MIN_POOL_SIZE) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the pool size must be no less than %uz",
|
||||
NGX_MIN_POOL_SIZE);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (*sp % NGX_POOL_ALIGNMENT) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the pool size must be a multiple of %uz",
|
||||
NGX_POOL_ALIGNMENT);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
ngx_uint_t *np = data;
|
||||
|
||||
ngx_uint_t mask;
|
||||
|
||||
mask = *np - 1;
|
||||
|
||||
if (*np == 0 || (*np & mask)) {
|
||||
return "must be a power of two";
|
||||
}
|
||||
|
||||
*np = mask;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data)
|
||||
{
|
||||
size_t *sp = data;
|
||||
|
||||
if (*sp == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the spdy chunk size cannot be zero");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (*sp > NGX_SPDY_MAX_FRAME_SIZE) {
|
||||
*sp = NGX_SPDY_MAX_FRAME_SIZE;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Nginx, Inc.
|
||||
* Copyright (C) Valentin V. Bartenev
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
|
||||
#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t recv_buffer_size;
|
||||
u_char *recv_buffer;
|
||||
} ngx_http_spdy_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t pool_size;
|
||||
ngx_uint_t concurrent_streams;
|
||||
ngx_uint_t streams_index_mask;
|
||||
ngx_msec_t recv_timeout;
|
||||
ngx_msec_t keepalive_timeout;
|
||||
ngx_int_t headers_comp;
|
||||
} ngx_http_spdy_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t chunk_size;
|
||||
} ngx_http_spdy_loc_conf_t;
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_spdy_module;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */
|
||||
@@ -0,0 +1,792 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <nginx.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,
|
||||
ngx_http_err_page_t *err_page);
|
||||
static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,
|
||||
ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);
|
||||
static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);
|
||||
|
||||
|
||||
static u_char ngx_http_error_full_tail[] =
|
||||
"<hr><center>" NGINX_VER "</center>" CRLF
|
||||
"</body>" CRLF
|
||||
"</html>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static u_char ngx_http_error_tail[] =
|
||||
"<hr><center>nginx</center>" CRLF
|
||||
"</body>" CRLF
|
||||
"</html>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static u_char ngx_http_msie_padding[] =
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
|
||||
;
|
||||
|
||||
|
||||
static u_char ngx_http_msie_refresh_head[] =
|
||||
"<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=";
|
||||
|
||||
|
||||
static u_char ngx_http_msie_refresh_tail[] =
|
||||
"\"></head><body></body></html>" CRLF;
|
||||
|
||||
|
||||
static char ngx_http_error_301_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>301 Moved Permanently</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>301 Moved Permanently</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_302_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>302 Found</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>302 Found</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_303_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>303 See Other</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>303 See Other</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_307_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>307 Temporary Redirect</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>307 Temporary Redirect</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_400_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>400 Bad Request</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>400 Bad Request</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_401_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>401 Authorization Required</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>401 Authorization Required</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_402_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>402 Payment Required</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>402 Payment Required</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_403_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>403 Forbidden</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>403 Forbidden</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_404_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>404 Not Found</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>404 Not Found</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_405_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>405 Not Allowed</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>405 Not Allowed</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_406_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>406 Not Acceptable</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>406 Not Acceptable</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_408_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>408 Request Time-out</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>408 Request Time-out</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_409_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>409 Conflict</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>409 Conflict</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_410_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>410 Gone</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>410 Gone</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_411_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>411 Length Required</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>411 Length Required</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_412_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>412 Precondition Failed</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>412 Precondition Failed</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_413_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>413 Request Entity Too Large</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>413 Request Entity Too Large</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_414_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>414 Request-URI Too Large</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>414 Request-URI Too Large</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_415_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>415 Unsupported Media Type</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>415 Unsupported Media Type</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_416_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_494_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>400 Request Header Or Cookie Too Large</title></head>"
|
||||
CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>400 Bad Request</h1></center>" CRLF
|
||||
"<center>Request Header Or Cookie Too Large</center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_495_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>400 The SSL certificate error</title></head>"
|
||||
CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>400 Bad Request</h1></center>" CRLF
|
||||
"<center>The SSL certificate error</center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_496_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>400 No required SSL certificate was sent</title></head>"
|
||||
CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>400 Bad Request</h1></center>" CRLF
|
||||
"<center>No required SSL certificate was sent</center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_497_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
|
||||
CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>400 Bad Request</h1></center>" CRLF
|
||||
"<center>The plain HTTP request was sent to HTTPS port</center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_500_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>500 Internal Server Error</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>500 Internal Server Error</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_501_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>501 Not Implemented</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>501 Not Implemented</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_502_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>502 Bad Gateway</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>502 Bad Gateway</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_503_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_504_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>504 Gateway Time-out</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>504 Gateway Time-out</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static char ngx_http_error_507_page[] =
|
||||
"<html>" CRLF
|
||||
"<head><title>507 Insufficient Storage</title></head>" CRLF
|
||||
"<body bgcolor=\"white\">" CRLF
|
||||
"<center><h1>507 Insufficient Storage</h1></center>" CRLF
|
||||
;
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_error_pages[] = {
|
||||
|
||||
ngx_null_string, /* 201, 204 */
|
||||
|
||||
#define NGX_HTTP_LAST_2XX 202
|
||||
#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 201)
|
||||
|
||||
/* ngx_null_string, */ /* 300 */
|
||||
ngx_string(ngx_http_error_301_page),
|
||||
ngx_string(ngx_http_error_302_page),
|
||||
ngx_string(ngx_http_error_303_page),
|
||||
ngx_null_string, /* 304 */
|
||||
ngx_null_string, /* 305 */
|
||||
ngx_null_string, /* 306 */
|
||||
ngx_string(ngx_http_error_307_page),
|
||||
|
||||
#define NGX_HTTP_LAST_3XX 308
|
||||
#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
|
||||
|
||||
ngx_string(ngx_http_error_400_page),
|
||||
ngx_string(ngx_http_error_401_page),
|
||||
ngx_string(ngx_http_error_402_page),
|
||||
ngx_string(ngx_http_error_403_page),
|
||||
ngx_string(ngx_http_error_404_page),
|
||||
ngx_string(ngx_http_error_405_page),
|
||||
ngx_string(ngx_http_error_406_page),
|
||||
ngx_null_string, /* 407 */
|
||||
ngx_string(ngx_http_error_408_page),
|
||||
ngx_string(ngx_http_error_409_page),
|
||||
ngx_string(ngx_http_error_410_page),
|
||||
ngx_string(ngx_http_error_411_page),
|
||||
ngx_string(ngx_http_error_412_page),
|
||||
ngx_string(ngx_http_error_413_page),
|
||||
ngx_string(ngx_http_error_414_page),
|
||||
ngx_string(ngx_http_error_415_page),
|
||||
ngx_string(ngx_http_error_416_page),
|
||||
|
||||
#define NGX_HTTP_LAST_4XX 417
|
||||
#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
|
||||
|
||||
ngx_string(ngx_http_error_494_page), /* 494, request header too large */
|
||||
ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
|
||||
ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
|
||||
ngx_string(ngx_http_error_497_page), /* 497, http to https */
|
||||
ngx_string(ngx_http_error_404_page), /* 498, canceled */
|
||||
ngx_null_string, /* 499, client has closed connection */
|
||||
|
||||
ngx_string(ngx_http_error_500_page),
|
||||
ngx_string(ngx_http_error_501_page),
|
||||
ngx_string(ngx_http_error_502_page),
|
||||
ngx_string(ngx_http_error_503_page),
|
||||
ngx_string(ngx_http_error_504_page),
|
||||
ngx_null_string, /* 505 */
|
||||
ngx_null_string, /* 506 */
|
||||
ngx_string(ngx_http_error_507_page)
|
||||
|
||||
#define NGX_HTTP_LAST_5XX 508
|
||||
|
||||
};
|
||||
|
||||
|
||||
static ngx_str_t ngx_http_get_name = { 3, (u_char *) "GET " };
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
|
||||
{
|
||||
ngx_uint_t i, err;
|
||||
ngx_http_err_page_t *err_page;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http special response: %i, \"%V?%V\"",
|
||||
error, &r->uri, &r->args);
|
||||
|
||||
r->err_status = error;
|
||||
|
||||
if (r->keepalive) {
|
||||
switch (error) {
|
||||
case NGX_HTTP_BAD_REQUEST:
|
||||
case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
|
||||
case NGX_HTTP_REQUEST_URI_TOO_LARGE:
|
||||
case NGX_HTTP_TO_HTTPS:
|
||||
case NGX_HTTPS_CERT_ERROR:
|
||||
case NGX_HTTPS_NO_CERT:
|
||||
case NGX_HTTP_INTERNAL_SERVER_ERROR:
|
||||
case NGX_HTTP_NOT_IMPLEMENTED:
|
||||
r->keepalive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->lingering_close) {
|
||||
switch (error) {
|
||||
case NGX_HTTP_BAD_REQUEST:
|
||||
case NGX_HTTP_TO_HTTPS:
|
||||
case NGX_HTTPS_CERT_ERROR:
|
||||
case NGX_HTTPS_NO_CERT:
|
||||
r->lingering_close = 0;
|
||||
}
|
||||
}
|
||||
|
||||
r->headers_out.content_type.len = 0;
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {
|
||||
|
||||
if (clcf->recursive_error_pages == 0) {
|
||||
r->error_page = 1;
|
||||
}
|
||||
|
||||
err_page = clcf->error_pages->elts;
|
||||
|
||||
for (i = 0; i < clcf->error_pages->nelts; i++) {
|
||||
if (err_page[i].status == error) {
|
||||
return ngx_http_send_error_page(r, &err_page[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r->expect_tested = 1;
|
||||
|
||||
if (ngx_http_discard_request_body(r) != NGX_OK) {
|
||||
r->keepalive = 0;
|
||||
}
|
||||
|
||||
if (clcf->msie_refresh
|
||||
&& r->headers_in.msie
|
||||
&& (error == NGX_HTTP_MOVED_PERMANENTLY
|
||||
|| error == NGX_HTTP_MOVED_TEMPORARILY))
|
||||
{
|
||||
return ngx_http_send_refresh(r);
|
||||
}
|
||||
|
||||
if (error == NGX_HTTP_CREATED) {
|
||||
/* 201 */
|
||||
err = 0;
|
||||
|
||||
} else if (error == NGX_HTTP_NO_CONTENT) {
|
||||
/* 204 */
|
||||
err = 0;
|
||||
|
||||
} else if (error >= NGX_HTTP_MOVED_PERMANENTLY
|
||||
&& error < NGX_HTTP_LAST_3XX)
|
||||
{
|
||||
/* 3XX */
|
||||
err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
|
||||
|
||||
} else if (error >= NGX_HTTP_BAD_REQUEST
|
||||
&& error < NGX_HTTP_LAST_4XX)
|
||||
{
|
||||
/* 4XX */
|
||||
err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
|
||||
|
||||
} else if (error >= NGX_HTTP_NGINX_CODES
|
||||
&& error < NGX_HTTP_LAST_5XX)
|
||||
{
|
||||
/* 49X, 5XX */
|
||||
err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
|
||||
switch (error) {
|
||||
case NGX_HTTP_TO_HTTPS:
|
||||
case NGX_HTTPS_CERT_ERROR:
|
||||
case NGX_HTTPS_NO_CERT:
|
||||
case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
|
||||
r->err_status = NGX_HTTP_BAD_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* unknown code, zero body */
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return ngx_http_send_special_response(r, clcf, err);
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
|
||||
ngx_int_t error)
|
||||
{
|
||||
void *ctx;
|
||||
ngx_int_t rc;
|
||||
|
||||
ngx_http_clean_header(r);
|
||||
|
||||
ctx = NULL;
|
||||
|
||||
if (m) {
|
||||
ctx = r->ctx[m->ctx_index];
|
||||
}
|
||||
|
||||
/* clear the modules contexts */
|
||||
ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
|
||||
|
||||
if (m) {
|
||||
r->ctx[m->ctx_index] = ctx;
|
||||
}
|
||||
|
||||
r->filter_finalize = 1;
|
||||
|
||||
rc = ngx_http_special_response_handler(r, error);
|
||||
|
||||
/* NGX_ERROR resets any pending data */
|
||||
|
||||
switch (rc) {
|
||||
|
||||
case NGX_OK:
|
||||
case NGX_DONE:
|
||||
return NGX_ERROR;
|
||||
|
||||
default:
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_http_clean_header(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_memzero(&r->headers_out.status,
|
||||
sizeof(ngx_http_headers_out_t)
|
||||
- offsetof(ngx_http_headers_out_t, status));
|
||||
|
||||
r->headers_out.headers.part.nelts = 0;
|
||||
r->headers_out.headers.part.next = NULL;
|
||||
r->headers_out.headers.last = &r->headers_out.headers.part;
|
||||
|
||||
r->headers_out.content_length_n = -1;
|
||||
r->headers_out.last_modified_time = -1;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
|
||||
{
|
||||
ngx_int_t overwrite;
|
||||
ngx_str_t uri, args;
|
||||
ngx_table_elt_t *location;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
overwrite = err_page->overwrite;
|
||||
|
||||
if (overwrite && overwrite != NGX_HTTP_OK) {
|
||||
r->expect_tested = 1;
|
||||
}
|
||||
|
||||
if (overwrite >= 0) {
|
||||
r->err_status = overwrite;
|
||||
}
|
||||
|
||||
if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (uri.len && uri.data[0] == '/') {
|
||||
|
||||
if (err_page->value.lengths) {
|
||||
ngx_http_split_args(r, &uri, &args);
|
||||
|
||||
} else {
|
||||
args = err_page->args;
|
||||
}
|
||||
|
||||
if (r->method != NGX_HTTP_HEAD) {
|
||||
r->method = NGX_HTTP_GET;
|
||||
r->method_name = ngx_http_get_name;
|
||||
}
|
||||
|
||||
return ngx_http_internal_redirect(r, &uri, &args);
|
||||
}
|
||||
|
||||
if (uri.len && uri.data[0] == '@') {
|
||||
return ngx_http_named_location(r, &uri);
|
||||
}
|
||||
|
||||
location = ngx_list_push(&r->headers_out.headers);
|
||||
|
||||
if (location == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
|
||||
&& overwrite != NGX_HTTP_MOVED_TEMPORARILY
|
||||
&& overwrite != NGX_HTTP_SEE_OTHER
|
||||
&& overwrite != NGX_HTTP_TEMPORARY_REDIRECT)
|
||||
{
|
||||
r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
|
||||
}
|
||||
|
||||
location->hash = 1;
|
||||
ngx_str_set(&location->key, "Location");
|
||||
location->value = uri;
|
||||
|
||||
ngx_http_clear_location(r);
|
||||
|
||||
r->headers_out.location = location;
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->msie_refresh && r->headers_in.msie) {
|
||||
return ngx_http_send_refresh(r);
|
||||
}
|
||||
|
||||
return ngx_http_send_special_response(r, clcf, r->err_status
|
||||
- NGX_HTTP_MOVED_PERMANENTLY
|
||||
+ NGX_HTTP_OFF_3XX);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_send_special_response(ngx_http_request_t *r,
|
||||
ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
|
||||
{
|
||||
u_char *tail;
|
||||
size_t len;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_uint_t msie_padding;
|
||||
ngx_chain_t out[3];
|
||||
|
||||
if (clcf->server_tokens) {
|
||||
len = sizeof(ngx_http_error_full_tail) - 1;
|
||||
tail = ngx_http_error_full_tail;
|
||||
|
||||
} else {
|
||||
len = sizeof(ngx_http_error_tail) - 1;
|
||||
tail = ngx_http_error_tail;
|
||||
}
|
||||
|
||||
msie_padding = 0;
|
||||
|
||||
if (ngx_http_error_pages[err].len) {
|
||||
r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
|
||||
if (clcf->msie_padding
|
||||
&& (r->headers_in.msie || r->headers_in.chrome)
|
||||
&& r->http_version >= NGX_HTTP_VERSION_10
|
||||
&& err >= NGX_HTTP_OFF_4XX)
|
||||
{
|
||||
r->headers_out.content_length_n +=
|
||||
sizeof(ngx_http_msie_padding) - 1;
|
||||
msie_padding = 1;
|
||||
}
|
||||
|
||||
r->headers_out.content_type_len = sizeof("text/html") - 1;
|
||||
ngx_str_set(&r->headers_out.content_type, "text/html");
|
||||
r->headers_out.content_type_lowcase = NULL;
|
||||
|
||||
} else {
|
||||
r->headers_out.content_length_n = 0;
|
||||
}
|
||||
|
||||
if (r->headers_out.content_length) {
|
||||
r->headers_out.content_length->hash = 0;
|
||||
r->headers_out.content_length = NULL;
|
||||
}
|
||||
|
||||
ngx_http_clear_accept_ranges(r);
|
||||
ngx_http_clear_last_modified(r);
|
||||
ngx_http_clear_etag(r);
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ngx_http_error_pages[err].len == 0) {
|
||||
return ngx_http_send_special(r, NGX_HTTP_LAST);
|
||||
}
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->memory = 1;
|
||||
b->pos = ngx_http_error_pages[err].data;
|
||||
b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
|
||||
|
||||
out[0].buf = b;
|
||||
out[0].next = &out[1];
|
||||
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->memory = 1;
|
||||
|
||||
b->pos = tail;
|
||||
b->last = tail + len;
|
||||
|
||||
out[1].buf = b;
|
||||
out[1].next = NULL;
|
||||
|
||||
if (msie_padding) {
|
||||
b = ngx_calloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b->memory = 1;
|
||||
b->pos = ngx_http_msie_padding;
|
||||
b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;
|
||||
|
||||
out[1].next = &out[2];
|
||||
out[2].buf = b;
|
||||
out[2].next = NULL;
|
||||
}
|
||||
|
||||
if (r == r->main) {
|
||||
b->last_buf = 1;
|
||||
}
|
||||
|
||||
b->last_in_chain = 1;
|
||||
|
||||
return ngx_http_output_filter(r, &out[0]);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_send_refresh(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p, *location;
|
||||
size_t len, size;
|
||||
uintptr_t escape;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t out;
|
||||
|
||||
len = r->headers_out.location->value.len;
|
||||
location = r->headers_out.location->value.data;
|
||||
|
||||
escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
|
||||
|
||||
size = sizeof(ngx_http_msie_refresh_head) - 1
|
||||
+ escape + len
|
||||
+ sizeof(ngx_http_msie_refresh_tail) - 1;
|
||||
|
||||
r->err_status = NGX_HTTP_OK;
|
||||
|
||||
r->headers_out.content_type_len = sizeof("text/html") - 1;
|
||||
ngx_str_set(&r->headers_out.content_type, "text/html");
|
||||
r->headers_out.content_type_lowcase = NULL;
|
||||
|
||||
r->headers_out.location->hash = 0;
|
||||
r->headers_out.location = NULL;
|
||||
|
||||
r->headers_out.content_length_n = size;
|
||||
|
||||
if (r->headers_out.content_length) {
|
||||
r->headers_out.content_length->hash = 0;
|
||||
r->headers_out.content_length = NULL;
|
||||
}
|
||||
|
||||
ngx_http_clear_accept_ranges(r);
|
||||
ngx_http_clear_last_modified(r);
|
||||
ngx_http_clear_etag(r);
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
|
||||
if (rc == NGX_ERROR || r->header_only) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, size);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
|
||||
sizeof(ngx_http_msie_refresh_head) - 1);
|
||||
|
||||
if (escape == 0) {
|
||||
p = ngx_cpymem(p, location, len);
|
||||
|
||||
} else {
|
||||
p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
|
||||
}
|
||||
|
||||
b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
|
||||
sizeof(ngx_http_msie_refresh_tail) - 1);
|
||||
|
||||
b->last_buf = 1;
|
||||
b->last_in_chain = 1;
|
||||
|
||||
out.buf = b;
|
||||
out.next = NULL;
|
||||
|
||||
return ngx_http_output_filter(r, &out);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,413 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_
|
||||
#define _NGX_HTTP_UPSTREAM_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_event_connect.h>
|
||||
#include <ngx_event_pipe.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_FT_ERROR 0x00000002
|
||||
#define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004
|
||||
#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100
|
||||
#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200
|
||||
#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000400
|
||||
#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000800
|
||||
#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00001000
|
||||
#define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000
|
||||
#define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_502 \
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_503 \
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_504 \
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_403 \
|
||||
|NGX_HTTP_UPSTREAM_FT_HTTP_404)
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
|
||||
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES 0x00000004
|
||||
#define NGX_HTTP_UPSTREAM_IGN_EXPIRES 0x00000008
|
||||
#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010
|
||||
#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE 0x00000020
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080
|
||||
#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100
|
||||
#define NGX_HTTP_UPSTREAM_IGN_VARY 0x00000200
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_msec_t bl_time;
|
||||
ngx_uint_t bl_state;
|
||||
|
||||
ngx_uint_t status;
|
||||
time_t response_sec;
|
||||
ngx_uint_t response_msec;
|
||||
time_t header_sec;
|
||||
ngx_uint_t header_msec;
|
||||
off_t response_length;
|
||||
|
||||
ngx_str_t *peer;
|
||||
} ngx_http_upstream_state_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_t headers_in_hash;
|
||||
ngx_array_t upstreams;
|
||||
/* ngx_http_upstream_srv_conf_t */
|
||||
} ngx_http_upstream_main_conf_t;
|
||||
|
||||
typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_init_pt init_upstream;
|
||||
ngx_http_upstream_init_peer_pt init;
|
||||
void *data;
|
||||
} ngx_http_upstream_peer_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_addr_t *addrs;
|
||||
ngx_uint_t naddrs;
|
||||
ngx_uint_t weight;
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
unsigned down:1;
|
||||
unsigned backup:1;
|
||||
} ngx_http_upstream_server_t;
|
||||
|
||||
|
||||
#define NGX_HTTP_UPSTREAM_CREATE 0x0001
|
||||
#define NGX_HTTP_UPSTREAM_WEIGHT 0x0002
|
||||
#define NGX_HTTP_UPSTREAM_MAX_FAILS 0x0004
|
||||
#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008
|
||||
#define NGX_HTTP_UPSTREAM_DOWN 0x0010
|
||||
#define NGX_HTTP_UPSTREAM_BACKUP 0x0020
|
||||
|
||||
|
||||
struct ngx_http_upstream_srv_conf_s {
|
||||
ngx_http_upstream_peer_t peer;
|
||||
void **srv_conf;
|
||||
|
||||
ngx_array_t *servers; /* ngx_http_upstream_server_t */
|
||||
|
||||
ngx_uint_t flags;
|
||||
ngx_str_t host;
|
||||
u_char *file_name;
|
||||
ngx_uint_t line;
|
||||
in_port_t port;
|
||||
in_port_t default_port;
|
||||
ngx_uint_t no_port; /* unsigned no_port:1 */
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_addr_t *addr;
|
||||
ngx_http_complex_value_t *value;
|
||||
} ngx_http_upstream_local_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_srv_conf_t *upstream;
|
||||
|
||||
ngx_msec_t connect_timeout;
|
||||
ngx_msec_t send_timeout;
|
||||
ngx_msec_t read_timeout;
|
||||
ngx_msec_t timeout;
|
||||
ngx_msec_t next_upstream_timeout;
|
||||
|
||||
size_t send_lowat;
|
||||
size_t buffer_size;
|
||||
size_t limit_rate;
|
||||
|
||||
size_t busy_buffers_size;
|
||||
size_t max_temp_file_size;
|
||||
size_t temp_file_write_size;
|
||||
|
||||
size_t busy_buffers_size_conf;
|
||||
size_t max_temp_file_size_conf;
|
||||
size_t temp_file_write_size_conf;
|
||||
|
||||
ngx_bufs_t bufs;
|
||||
|
||||
ngx_uint_t ignore_headers;
|
||||
ngx_uint_t next_upstream;
|
||||
ngx_uint_t store_access;
|
||||
ngx_uint_t next_upstream_tries;
|
||||
ngx_flag_t buffering;
|
||||
ngx_flag_t request_buffering;
|
||||
ngx_flag_t pass_request_headers;
|
||||
ngx_flag_t pass_request_body;
|
||||
|
||||
ngx_flag_t ignore_client_abort;
|
||||
ngx_flag_t intercept_errors;
|
||||
ngx_flag_t cyclic_temp_file;
|
||||
ngx_flag_t force_ranges;
|
||||
|
||||
ngx_path_t *temp_path;
|
||||
|
||||
ngx_hash_t hide_headers_hash;
|
||||
ngx_array_t *hide_headers;
|
||||
ngx_array_t *pass_headers;
|
||||
|
||||
ngx_http_upstream_local_t *local;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_shm_zone_t *cache_zone;
|
||||
ngx_http_complex_value_t *cache_value;
|
||||
|
||||
ngx_uint_t cache_min_uses;
|
||||
ngx_uint_t cache_use_stale;
|
||||
ngx_uint_t cache_methods;
|
||||
|
||||
ngx_flag_t cache_lock;
|
||||
ngx_msec_t cache_lock_timeout;
|
||||
ngx_msec_t cache_lock_age;
|
||||
|
||||
ngx_flag_t cache_revalidate;
|
||||
|
||||
ngx_array_t *cache_valid;
|
||||
ngx_array_t *cache_bypass;
|
||||
ngx_array_t *no_cache;
|
||||
#endif
|
||||
|
||||
ngx_array_t *store_lengths;
|
||||
ngx_array_t *store_values;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
signed cache:2;
|
||||
#endif
|
||||
signed store:2;
|
||||
unsigned intercept_404:1;
|
||||
unsigned change_buffering:1;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_ssl_t *ssl;
|
||||
ngx_flag_t ssl_session_reuse;
|
||||
|
||||
ngx_http_complex_value_t *ssl_name;
|
||||
ngx_flag_t ssl_server_name;
|
||||
ngx_flag_t ssl_verify;
|
||||
#endif
|
||||
|
||||
ngx_str_t module;
|
||||
} ngx_http_upstream_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_http_header_handler_pt handler;
|
||||
ngx_uint_t offset;
|
||||
ngx_http_header_handler_pt copy_handler;
|
||||
ngx_uint_t conf;
|
||||
ngx_uint_t redirect; /* unsigned redirect:1; */
|
||||
} ngx_http_upstream_header_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_list_t headers;
|
||||
|
||||
ngx_uint_t status_n;
|
||||
ngx_str_t status_line;
|
||||
|
||||
ngx_table_elt_t *status;
|
||||
ngx_table_elt_t *date;
|
||||
ngx_table_elt_t *server;
|
||||
ngx_table_elt_t *connection;
|
||||
|
||||
ngx_table_elt_t *expires;
|
||||
ngx_table_elt_t *etag;
|
||||
ngx_table_elt_t *x_accel_expires;
|
||||
ngx_table_elt_t *x_accel_redirect;
|
||||
ngx_table_elt_t *x_accel_limit_rate;
|
||||
|
||||
ngx_table_elt_t *content_type;
|
||||
ngx_table_elt_t *content_length;
|
||||
|
||||
ngx_table_elt_t *last_modified;
|
||||
ngx_table_elt_t *location;
|
||||
ngx_table_elt_t *accept_ranges;
|
||||
ngx_table_elt_t *www_authenticate;
|
||||
ngx_table_elt_t *transfer_encoding;
|
||||
ngx_table_elt_t *vary;
|
||||
|
||||
#if (NGX_HTTP_GZIP)
|
||||
ngx_table_elt_t *content_encoding;
|
||||
#endif
|
||||
|
||||
ngx_array_t cache_control;
|
||||
ngx_array_t cookies;
|
||||
|
||||
off_t content_length_n;
|
||||
time_t last_modified_time;
|
||||
|
||||
unsigned connection_close:1;
|
||||
unsigned chunked:1;
|
||||
} ngx_http_upstream_headers_in_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t host;
|
||||
in_port_t port;
|
||||
ngx_uint_t no_port; /* unsigned no_port:1 */
|
||||
|
||||
ngx_uint_t naddrs;
|
||||
ngx_addr_t *addrs;
|
||||
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
|
||||
ngx_resolver_ctx_t *ctx;
|
||||
} ngx_http_upstream_resolved_t;
|
||||
|
||||
|
||||
typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
|
||||
ngx_http_upstream_t *u);
|
||||
|
||||
|
||||
struct ngx_http_upstream_s {
|
||||
ngx_http_upstream_handler_pt read_event_handler;
|
||||
ngx_http_upstream_handler_pt write_event_handler;
|
||||
|
||||
ngx_peer_connection_t peer;
|
||||
|
||||
ngx_event_pipe_t *pipe;
|
||||
|
||||
ngx_chain_t *request_bufs;
|
||||
|
||||
ngx_output_chain_ctx_t output;
|
||||
ngx_chain_writer_ctx_t writer;
|
||||
|
||||
ngx_http_upstream_conf_t *conf;
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_array_t *caches;
|
||||
#endif
|
||||
|
||||
ngx_http_upstream_headers_in_t headers_in;
|
||||
|
||||
ngx_http_upstream_resolved_t *resolved;
|
||||
|
||||
ngx_buf_t from_client;
|
||||
|
||||
ngx_buf_t buffer;
|
||||
off_t length;
|
||||
|
||||
ngx_chain_t *out_bufs;
|
||||
ngx_chain_t *busy_bufs;
|
||||
ngx_chain_t *free_bufs;
|
||||
|
||||
ngx_int_t (*input_filter_init)(void *data);
|
||||
ngx_int_t (*input_filter)(void *data, ssize_t bytes);
|
||||
void *input_filter_ctx;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_int_t (*create_key)(ngx_http_request_t *r);
|
||||
#endif
|
||||
ngx_int_t (*create_request)(ngx_http_request_t *r);
|
||||
ngx_int_t (*reinit_request)(ngx_http_request_t *r);
|
||||
ngx_int_t (*process_header)(ngx_http_request_t *r);
|
||||
void (*abort_request)(ngx_http_request_t *r);
|
||||
void (*finalize_request)(ngx_http_request_t *r,
|
||||
ngx_int_t rc);
|
||||
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h, size_t prefix);
|
||||
ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h);
|
||||
|
||||
ngx_msec_t timeout;
|
||||
|
||||
ngx_http_upstream_state_t *state;
|
||||
|
||||
ngx_str_t method;
|
||||
ngx_str_t schema;
|
||||
ngx_str_t uri;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_str_t ssl_name;
|
||||
#endif
|
||||
|
||||
ngx_http_cleanup_pt *cleanup;
|
||||
|
||||
unsigned store:1;
|
||||
unsigned cacheable:1;
|
||||
unsigned accel:1;
|
||||
unsigned ssl:1;
|
||||
#if (NGX_HTTP_CACHE)
|
||||
unsigned cache_status:3;
|
||||
#endif
|
||||
|
||||
unsigned buffering:1;
|
||||
unsigned keepalive:1;
|
||||
unsigned upgrade:1;
|
||||
|
||||
unsigned request_sent:1;
|
||||
unsigned header_sent:1;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t status;
|
||||
ngx_uint_t mask;
|
||||
} ngx_http_upstream_next_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t key;
|
||||
ngx_str_t value;
|
||||
ngx_uint_t skip_empty;
|
||||
} ngx_http_upstream_param_t;
|
||||
|
||||
|
||||
ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
|
||||
ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
|
||||
void ngx_http_upstream_init(ngx_http_request_t *r);
|
||||
ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
|
||||
ngx_url_t *u, ngx_uint_t flags);
|
||||
char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
|
||||
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
|
||||
ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
|
||||
|
||||
|
||||
#define ngx_http_conf_upstream_srv_conf(uscf, module) \
|
||||
uscf->srv_conf[module.ctx_index]
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_upstream_module;
|
||||
extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[];
|
||||
extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[];
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
|
||||
@@ -0,0 +1,696 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define ngx_http_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
|
||||
|
||||
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
|
||||
ngx_http_upstream_rr_peer_data_t *rrp);
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_url_t u;
|
||||
ngx_uint_t i, j, n, w;
|
||||
ngx_http_upstream_server_t *server;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
ngx_http_upstream_rr_peers_t *peers, *backup;
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_round_robin_peer;
|
||||
|
||||
if (us->servers) {
|
||||
server = us->servers->elts;
|
||||
|
||||
n = 0;
|
||||
w = 0;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n += server[i].naddrs;
|
||||
w += server[i].naddrs * server[i].weight;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no servers in upstream \"%V\" in %s:%ui",
|
||||
&us->host, us->file_name, us->line);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
|
||||
+ sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (n == 1);
|
||||
peers->number = n;
|
||||
peers->weighted = (w != n);
|
||||
peers->total_weight = w;
|
||||
peers->name = &us->host;
|
||||
|
||||
n = 0;
|
||||
peer = peers->peer;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < server[i].naddrs; j++) {
|
||||
peer[n].sockaddr = server[i].addrs[j].sockaddr;
|
||||
peer[n].socklen = server[i].addrs[j].socklen;
|
||||
peer[n].name = server[i].addrs[j].name;
|
||||
peer[n].weight = server[i].weight;
|
||||
peer[n].effective_weight = server[i].weight;
|
||||
peer[n].current_weight = 0;
|
||||
peer[n].max_fails = server[i].max_fails;
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
|
||||
/* backup servers */
|
||||
|
||||
n = 0;
|
||||
w = 0;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (!server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n += server[i].naddrs;
|
||||
w += server[i].naddrs * server[i].weight;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
|
||||
+ sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
|
||||
if (backup == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = 0;
|
||||
backup->single = 0;
|
||||
backup->number = n;
|
||||
backup->weighted = (w != n);
|
||||
backup->total_weight = w;
|
||||
backup->name = &us->host;
|
||||
|
||||
n = 0;
|
||||
peer = backup->peer;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (!server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < server[i].naddrs; j++) {
|
||||
peer[n].sockaddr = server[i].addrs[j].sockaddr;
|
||||
peer[n].socklen = server[i].addrs[j].socklen;
|
||||
peer[n].name = server[i].addrs[j].name;
|
||||
peer[n].weight = server[i].weight;
|
||||
peer[n].effective_weight = server[i].weight;
|
||||
peer[n].current_weight = 0;
|
||||
peer[n].max_fails = server[i].max_fails;
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
peers->next = backup;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
/* an upstream implicitly defined by proxy_pass, etc. */
|
||||
|
||||
if (us->port == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no port in upstream \"%V\" in %s:%ui",
|
||||
&us->host, us->file_name, us->line);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
u.host = us->host;
|
||||
u.port = us->port;
|
||||
|
||||
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
|
||||
if (u.err) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"%s in upstream \"%V\" in %s:%ui",
|
||||
u.err, &us->host, us->file_name, us->line);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = u.naddrs;
|
||||
|
||||
peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
|
||||
+ sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (n == 1);
|
||||
peers->number = n;
|
||||
peers->weighted = 0;
|
||||
peers->total_weight = n;
|
||||
peers->name = &us->host;
|
||||
|
||||
peer = peers->peer;
|
||||
|
||||
for (i = 0; i < u.naddrs; i++) {
|
||||
peer[i].sockaddr = u.addrs[i].sockaddr;
|
||||
peer[i].socklen = u.addrs[i].socklen;
|
||||
peer[i].name = u.addrs[i].name;
|
||||
peer[i].weight = 1;
|
||||
peer[i].effective_weight = 1;
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
|
||||
/* implicitly defined upstream has no backup servers */
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
ngx_http_upstream_rr_peer_data_t *rrp;
|
||||
|
||||
rrp = r->upstream->peer.data;
|
||||
|
||||
if (rrp == NULL) {
|
||||
rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
|
||||
if (rrp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.data = rrp;
|
||||
}
|
||||
|
||||
rrp->peers = us->peer.data;
|
||||
rrp->current = 0;
|
||||
|
||||
n = rrp->peers->number;
|
||||
|
||||
if (rrp->peers->next && rrp->peers->next->number > n) {
|
||||
n = rrp->peers->next->number;
|
||||
}
|
||||
|
||||
if (n <= 8 * sizeof(uintptr_t)) {
|
||||
rrp->tried = &rrp->data;
|
||||
rrp->data = 0;
|
||||
|
||||
} else {
|
||||
n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
|
||||
if (rrp->tried == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
|
||||
r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
|
||||
r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
|
||||
#if (NGX_HTTP_SSL)
|
||||
r->upstream->peer.set_session =
|
||||
ngx_http_upstream_set_round_robin_peer_session;
|
||||
r->upstream->peer.save_session =
|
||||
ngx_http_upstream_save_round_robin_peer_session;
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_resolved_t *ur)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
socklen_t socklen;
|
||||
ngx_uint_t i, n;
|
||||
struct sockaddr *sockaddr;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
ngx_http_upstream_rr_peer_data_t *rrp;
|
||||
|
||||
rrp = r->upstream->peer.data;
|
||||
|
||||
if (rrp == NULL) {
|
||||
rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
|
||||
if (rrp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
r->upstream->peer.data = rrp;
|
||||
}
|
||||
|
||||
peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)
|
||||
+ sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (ur->naddrs == 1);
|
||||
peers->number = ur->naddrs;
|
||||
peers->name = &ur->host;
|
||||
|
||||
peer = peers->peer;
|
||||
|
||||
if (ur->sockaddr) {
|
||||
peer[0].sockaddr = ur->sockaddr;
|
||||
peer[0].socklen = ur->socklen;
|
||||
peer[0].name = ur->host;
|
||||
peer[0].weight = 1;
|
||||
peer[0].effective_weight = 1;
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
|
||||
} else {
|
||||
|
||||
for (i = 0; i < ur->naddrs; i++) {
|
||||
|
||||
socklen = ur->addrs[i].socklen;
|
||||
|
||||
sockaddr = ngx_palloc(r->pool, socklen);
|
||||
if (sockaddr == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
|
||||
|
||||
switch (sockaddr->sa_family) {
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);
|
||||
break;
|
||||
#endif
|
||||
default: /* AF_INET */
|
||||
((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);
|
||||
}
|
||||
|
||||
p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
peer[i].sockaddr = sockaddr;
|
||||
peer[i].socklen = socklen;
|
||||
peer[i].name.len = len;
|
||||
peer[i].name.data = p;
|
||||
peer[i].weight = 1;
|
||||
peer[i].effective_weight = 1;
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
}
|
||||
}
|
||||
|
||||
rrp->peers = peers;
|
||||
rrp->current = 0;
|
||||
|
||||
if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
|
||||
rrp->tried = &rrp->data;
|
||||
rrp->data = 0;
|
||||
|
||||
} else {
|
||||
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
|
||||
if (rrp->tried == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
|
||||
r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
|
||||
r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
|
||||
#if (NGX_HTTP_SSL)
|
||||
r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;
|
||||
r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_http_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t i, n;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get rr peer, try: %ui", pc->tries);
|
||||
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
peers = rrp->peers;
|
||||
|
||||
/* ngx_lock_mutex(peers->mutex); */
|
||||
|
||||
if (peers->single) {
|
||||
peer = &peers->peer[0];
|
||||
|
||||
if (peer->down) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
|
||||
peer = ngx_http_upstream_get_peer(rrp);
|
||||
|
||||
if (peer == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"get rr peer, current: %ui %i",
|
||||
rrp->current, peer->current_weight);
|
||||
}
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
pc->name = &peer->name;
|
||||
|
||||
/* ngx_unlock_mutex(peers->mutex); */
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
if (peers->next) {
|
||||
|
||||
/* ngx_unlock_mutex(peers->mutex); */
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
|
||||
|
||||
rrp->peers = peers->next;
|
||||
|
||||
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rrp->tried[i] = 0;
|
||||
}
|
||||
|
||||
rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
|
||||
|
||||
if (rc != NGX_BUSY) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ngx_lock_mutex(peers->mutex); */
|
||||
}
|
||||
|
||||
/* all peers failed, mark them as live for quick recovery */
|
||||
|
||||
for (i = 0; i < peers->number; i++) {
|
||||
peers->peer[i].fails = 0;
|
||||
}
|
||||
|
||||
/* ngx_unlock_mutex(peers->mutex); */
|
||||
|
||||
pc->name = peers->name;
|
||||
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
|
||||
static ngx_http_upstream_rr_peer_t *
|
||||
ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
|
||||
{
|
||||
time_t now;
|
||||
uintptr_t m;
|
||||
ngx_int_t total;
|
||||
ngx_uint_t i, n;
|
||||
ngx_http_upstream_rr_peer_t *peer, *best;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
best = NULL;
|
||||
total = 0;
|
||||
|
||||
for (i = 0; i < rrp->peers->number; i++) {
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (rrp->tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer = &rrp->peers->peer[i];
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (best == NULL || peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = best - &rrp->peers->peer[0];
|
||||
|
||||
rrp->current = i;
|
||||
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried[n] |= m;
|
||||
|
||||
best->current_weight -= total;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
|
||||
ngx_uint_t state)
|
||||
{
|
||||
ngx_http_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
time_t now;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"free rr peer %ui %ui", pc->tries, state);
|
||||
|
||||
/* TODO: NGX_PEER_KEEPALIVE */
|
||||
|
||||
if (rrp->peers->single) {
|
||||
pc->tries = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
peer = &rrp->peers->peer[rrp->current];
|
||||
|
||||
if (state & NGX_PEER_FAILED) {
|
||||
now = ngx_time();
|
||||
|
||||
/* ngx_lock_mutex(rrp->peers->mutex); */
|
||||
|
||||
peer->fails++;
|
||||
peer->accessed = now;
|
||||
peer->checked = now;
|
||||
|
||||
if (peer->max_fails) {
|
||||
peer->effective_weight -= peer->weight / peer->max_fails;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"free rr peer failed: %ui %i",
|
||||
rrp->current, peer->effective_weight);
|
||||
|
||||
if (peer->effective_weight < 0) {
|
||||
peer->effective_weight = 0;
|
||||
}
|
||||
|
||||
/* ngx_unlock_mutex(rrp->peers->mutex); */
|
||||
|
||||
} else {
|
||||
|
||||
/* mark peer live if check passed */
|
||||
|
||||
if (peer->accessed < peer->checked) {
|
||||
peer->fails = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pc->tries) {
|
||||
pc->tries--;
|
||||
}
|
||||
|
||||
/* ngx_unlock_mutex(rrp->peers->mutex); */
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data)
|
||||
{
|
||||
ngx_http_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_ssl_session_t *ssl_session;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
|
||||
peer = &rrp->peers->peer[rrp->current];
|
||||
|
||||
/* TODO: threads only mutex */
|
||||
/* ngx_lock_mutex(rrp->peers->mutex); */
|
||||
|
||||
ssl_session = peer->ssl_session;
|
||||
|
||||
rc = ngx_ssl_set_session(pc->connection, ssl_session);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"set session: %p", ssl_session);
|
||||
|
||||
/* ngx_unlock_mutex(rrp->peers->mutex); */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data)
|
||||
{
|
||||
ngx_http_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_ssl_session_t *old_ssl_session, *ssl_session;
|
||||
ngx_http_upstream_rr_peer_t *peer;
|
||||
|
||||
ssl_session = ngx_ssl_get_session(pc->connection);
|
||||
|
||||
if (ssl_session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"save session: %p", ssl_session);
|
||||
|
||||
peer = &rrp->peers->peer[rrp->current];
|
||||
|
||||
/* TODO: threads only mutex */
|
||||
/* ngx_lock_mutex(rrp->peers->mutex); */
|
||||
|
||||
old_ssl_session = peer->ssl_session;
|
||||
peer->ssl_session = ssl_session;
|
||||
|
||||
/* ngx_unlock_mutex(rrp->peers->mutex); */
|
||||
|
||||
if (old_ssl_session) {
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
|
||||
"old session: %p", old_ssl_session);
|
||||
|
||||
/* TODO: may block */
|
||||
|
||||
ngx_ssl_free_session(old_ssl_session);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
|
||||
#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
ngx_str_t name;
|
||||
ngx_str_t server;
|
||||
|
||||
ngx_int_t current_weight;
|
||||
ngx_int_t effective_weight;
|
||||
ngx_int_t weight;
|
||||
|
||||
ngx_uint_t fails;
|
||||
time_t accessed;
|
||||
time_t checked;
|
||||
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
|
||||
ngx_uint_t down; /* unsigned down:1; */
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_ssl_session_t *ssl_session; /* local to a process */
|
||||
#endif
|
||||
} ngx_http_upstream_rr_peer_t;
|
||||
|
||||
|
||||
typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
|
||||
|
||||
struct ngx_http_upstream_rr_peers_s {
|
||||
ngx_uint_t number;
|
||||
|
||||
ngx_uint_t total_weight;
|
||||
|
||||
unsigned single:1;
|
||||
unsigned weighted:1;
|
||||
|
||||
ngx_str_t *name;
|
||||
|
||||
ngx_http_upstream_rr_peers_t *next;
|
||||
|
||||
ngx_http_upstream_rr_peer_t peer[1];
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_rr_peers_t *peers;
|
||||
ngx_uint_t current;
|
||||
uintptr_t *tried;
|
||||
uintptr_t data;
|
||||
} ngx_http_upstream_rr_peer_data_t;
|
||||
|
||||
|
||||
ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
|
||||
ngx_http_upstream_resolved_t *ur);
|
||||
ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t state);
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_int_t
|
||||
ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_
|
||||
#define _NGX_HTTP_VARIABLES_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef ngx_variable_value_t ngx_http_variable_value_t;
|
||||
|
||||
#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
|
||||
|
||||
typedef struct ngx_http_variable_s ngx_http_variable_t;
|
||||
|
||||
typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
|
||||
|
||||
#define NGX_HTTP_VAR_CHANGEABLE 1
|
||||
#define NGX_HTTP_VAR_NOCACHEABLE 2
|
||||
#define NGX_HTTP_VAR_INDEXED 4
|
||||
#define NGX_HTTP_VAR_NOHASH 8
|
||||
|
||||
|
||||
struct ngx_http_variable_s {
|
||||
ngx_str_t name; /* must be first to build the hash */
|
||||
ngx_http_set_variable_pt set_handler;
|
||||
ngx_http_get_variable_pt get_handler;
|
||||
uintptr_t data;
|
||||
ngx_uint_t flags;
|
||||
ngx_uint_t index;
|
||||
};
|
||||
|
||||
|
||||
ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,
|
||||
ngx_uint_t flags);
|
||||
ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
|
||||
ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,
|
||||
ngx_uint_t index);
|
||||
ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,
|
||||
ngx_uint_t index);
|
||||
|
||||
ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,
|
||||
ngx_str_t *name, ngx_uint_t key);
|
||||
|
||||
ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,
|
||||
ngx_str_t *var, ngx_list_part_t *part, size_t prefix);
|
||||
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t capture;
|
||||
ngx_int_t index;
|
||||
} ngx_http_regex_variable_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_regex_t *regex;
|
||||
ngx_uint_t ncaptures;
|
||||
ngx_http_regex_variable_t *variables;
|
||||
ngx_uint_t nvariables;
|
||||
ngx_str_t name;
|
||||
} ngx_http_regex_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_regex_t *regex;
|
||||
void *value;
|
||||
} ngx_http_map_regex_t;
|
||||
|
||||
|
||||
ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,
|
||||
ngx_regex_compile_t *rc);
|
||||
ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,
|
||||
ngx_str_t *s);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_combined_t hash;
|
||||
#if (NGX_PCRE)
|
||||
ngx_http_map_regex_t *regex;
|
||||
ngx_uint_t nregex;
|
||||
#endif
|
||||
} ngx_http_map_t;
|
||||
|
||||
|
||||
void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,
|
||||
ngx_str_t *match);
|
||||
|
||||
|
||||
ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);
|
||||
ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
|
||||
|
||||
|
||||
extern ngx_http_variable_value_t ngx_http_variable_null_value;
|
||||
extern ngx_http_variable_value_t ngx_http_variable_true_value;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */
|
||||
@@ -0,0 +1,327 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_write_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_write_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL, /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_write_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_write_filter_module_ctx, /* module context */
|
||||
NULL, /* 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
|
||||
};
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
{
|
||||
off_t size, sent, nsent, limit;
|
||||
ngx_uint_t last, flush, sync;
|
||||
ngx_msec_t delay;
|
||||
ngx_chain_t *cl, *ln, **ll, *chain;
|
||||
ngx_connection_t *c;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
c = r->connection;
|
||||
|
||||
if (c->error) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
flush = 0;
|
||||
sync = 0;
|
||||
last = 0;
|
||||
ll = &r->out;
|
||||
|
||||
/* find the size, the flush point and the last link of the saved chain */
|
||||
|
||||
for (cl = r->out; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"write old buf t:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
cl->buf->temporary, cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
#if 1
|
||||
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"zero size buf in writer "
|
||||
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
||||
cl->buf->temporary,
|
||||
cl->buf->recycled,
|
||||
cl->buf->in_file,
|
||||
cl->buf->start,
|
||||
cl->buf->pos,
|
||||
cl->buf->last,
|
||||
cl->buf->file,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last);
|
||||
|
||||
ngx_debug_point();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
size += ngx_buf_size(cl->buf);
|
||||
|
||||
if (cl->buf->flush || cl->buf->recycled) {
|
||||
flush = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->sync) {
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* add the new chain to the existent one */
|
||||
|
||||
for (ln = in; ln; ln = ln->next) {
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = ln->buf;
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"write new buf t:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
cl->buf->temporary, cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
#if 1
|
||||
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"zero size buf in writer "
|
||||
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
||||
cl->buf->temporary,
|
||||
cl->buf->recycled,
|
||||
cl->buf->in_file,
|
||||
cl->buf->start,
|
||||
cl->buf->pos,
|
||||
cl->buf->last,
|
||||
cl->buf->file,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last);
|
||||
|
||||
ngx_debug_point();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
size += ngx_buf_size(cl->buf);
|
||||
|
||||
if (cl->buf->flush || cl->buf->recycled) {
|
||||
flush = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->sync) {
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
*ll = NULL;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http write filter: l:%d f:%d s:%O", last, flush, size);
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
/*
|
||||
* avoid the output if there are no last buf, no flush point,
|
||||
* there are the incoming bufs and the size of all bufs
|
||||
* is smaller than "postpone_output" directive
|
||||
*/
|
||||
|
||||
if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (c->write->delayed) {
|
||||
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (size == 0
|
||||
&& !(c->buffered & NGX_LOWLEVEL_BUFFERED)
|
||||
&& !(last && c->need_last_buf))
|
||||
{
|
||||
if (last || flush || sync) {
|
||||
for (cl = r->out; cl; /* void */) {
|
||||
ln = cl;
|
||||
cl = cl->next;
|
||||
ngx_free_chain(r->pool, ln);
|
||||
}
|
||||
|
||||
r->out = NULL;
|
||||
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"the http output chain is empty");
|
||||
|
||||
ngx_debug_point();
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (r->limit_rate) {
|
||||
if (r->limit_rate_after == 0) {
|
||||
r->limit_rate_after = clcf->limit_rate_after;
|
||||
}
|
||||
|
||||
limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
|
||||
- (c->sent - r->limit_rate_after);
|
||||
|
||||
if (limit <= 0) {
|
||||
c->write->delayed = 1;
|
||||
delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
|
||||
ngx_add_timer(c->write, delay);
|
||||
|
||||
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (clcf->sendfile_max_chunk
|
||||
&& (off_t) clcf->sendfile_max_chunk < limit)
|
||||
{
|
||||
limit = clcf->sendfile_max_chunk;
|
||||
}
|
||||
|
||||
} else {
|
||||
limit = clcf->sendfile_max_chunk;
|
||||
}
|
||||
|
||||
sent = c->sent;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http write filter limit %O", limit);
|
||||
|
||||
chain = c->send_chain(c, r->out, limit);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http write filter %p", chain);
|
||||
|
||||
if (chain == NGX_CHAIN_ERROR) {
|
||||
c->error = 1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (r->limit_rate) {
|
||||
|
||||
nsent = c->sent;
|
||||
|
||||
if (r->limit_rate_after) {
|
||||
|
||||
sent -= r->limit_rate_after;
|
||||
if (sent < 0) {
|
||||
sent = 0;
|
||||
}
|
||||
|
||||
nsent -= r->limit_rate_after;
|
||||
if (nsent < 0) {
|
||||
nsent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
|
||||
|
||||
if (delay > 0) {
|
||||
limit = 0;
|
||||
c->write->delayed = 1;
|
||||
ngx_add_timer(c->write, delay);
|
||||
}
|
||||
}
|
||||
|
||||
if (limit
|
||||
&& c->write->ready
|
||||
&& c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
|
||||
{
|
||||
c->write->delayed = 1;
|
||||
ngx_add_timer(c->write, 1);
|
||||
}
|
||||
|
||||
for (cl = r->out; cl && cl != chain; /* void */) {
|
||||
ln = cl;
|
||||
cl = cl->next;
|
||||
ngx_free_chain(r->pool, ln);
|
||||
}
|
||||
|
||||
r->out = chain;
|
||||
|
||||
if (chain) {
|
||||
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
|
||||
|
||||
if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_write_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_top_body_filter = ngx_http_write_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user