Initial commit

This commit is contained in:
WatermelonModders
2022-05-31 12:35:46 -04:00
commit fc5cb0c32c
4097 changed files with 447075 additions and 0 deletions
@@ -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'
}
);
+138
View File
@@ -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_ */
+3
View File
@@ -0,0 +1,3 @@
TYPEMAP
nginx T_PTROBJ
File diff suppressed because it is too large Load Diff
+184
View File
@@ -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_ */
+186
View File
@@ -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_ */
+75
View File
@@ -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
+600
View File
@@ -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
+277
View File
@@ -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
+604
View File
@@ -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
+257
View File
@@ -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
+261
View File
@@ -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
+408
View File
@@ -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;
}
+41
View File
@@ -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
+413
View File
@@ -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
+112
View File
@@ -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;
}