Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#user nobody;
worker_processes 1;

#error_log logs/error.log;
error_log logs/error.log debug;
#error_log logs/error.log notice;
#error_log logs/error.log info;

Expand All @@ -14,7 +14,18 @@ events {
}



http {

map $http_authorization $limita {
default "";
"~.+" bar;
}

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=12r/s;
limit_req_zone $binary_remote_addr zone=minute:10m rate=30r/m;
limit_req_zone foo zone=authorization:10m rate=60r/m;

include mime.types;
default_type application/octet-stream;

Expand Down Expand Up @@ -43,6 +54,18 @@ http {
location / {
root html;
index index.html index.htm;
limit_req zone=mylimit;
#limit_req zone=authorization nodelay burst=30;


}

location /minute {
root html;
index index.html index.htm;
limit_req zone=minute nodelay burst=2;


}

#error_page 404 /404.html;
Expand Down
128 changes: 122 additions & 6 deletions src/http/modules/ngx_http_limit_req_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ typedef struct {

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);
ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t *remaining, 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,
Expand Down Expand Up @@ -129,7 +129,6 @@ static ngx_command_t ngx_http_limit_req_commands[] = {
ngx_null_command
};


static ngx_http_module_t ngx_http_limit_req_module_ctx = {
NULL, /* preconfiguration */
ngx_http_limit_req_init, /* postconfiguration */
Expand Down Expand Up @@ -168,6 +167,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
ngx_str_t key;
ngx_int_t rc;
ngx_uint_t n, excess;
ngx_uint_t min_remaining, remaining, rate, burst;
ngx_msec_t delay;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_limit_req_conf_t *lrcf;
Expand All @@ -181,6 +181,11 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
limits = lrcf->limits.elts;

excess = 0;
min_remaining = 0xffffffff;
remaining = 0;
rate = 1;
delay = 0;
burst = 0;

rc = NGX_DECLINED;

Expand Down Expand Up @@ -214,7 +219,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)

ngx_shmtx_lock(&ctx->shpool->mutex);

rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,
rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, &remaining,
(n == lrcf->limits.nelts - 1));

ngx_shmtx_unlock(&ctx->shpool->mutex);
Expand All @@ -223,10 +228,112 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
"limit_req[%ui]: %i %ui.%03ui",
n, rc, excess / 1000, excess % 1000);

ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"limit_req[%ui]: %i excess=%ui rate=%ui "
"ctx_rate=%ui remaining=%ui remaining=%ui burst=%ui",
n, rc, excess, rate,
ctx->rate, min_remaining, remaining, limit->burst );

/* Get the lowest remaining value. We should even get values below quotas. */
if (min_remaining == 0xffffffff
|| min_remaining + excess > remaining + limit->burst
// || (limit->burst && excess >= limit->burst)
){
rate = ctx->rate;
burst = limit->burst;

if (remaining + limit->burst < excess) {
min_remaining = 0;
} else {
min_remaining = remaining + limit->burst - excess;
}
}

if (rc != NGX_AGAIN) {
break;
}
}
/** begin header addition */
if (r->main->limit_req_set == 0) {
/* subsecond rates should be expressed with a x-ratelimit-reset > 1 */
ngx_int_t limit = (rate < 1000 ? 1 : rate / 1000);
ngx_int_t limit_r = (rate < 1000 ? 1 : min_remaining / 1000);
if (1) {
ngx_table_elt_t *h;

h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}

h->hash = 1;
h->key = (ngx_str_t) ngx_string("x-ratelimit-limit");

ngx_buf_t *b = ngx_create_temp_buf(r->pool, 256);
if (b == NULL) {
return NGX_ERROR;
}
b->last = ngx_sprintf(b->last, "%d, %d;window=%d;burst=%ui",
(int) (rate < 1000 ? 1 : rate / 1000),
(int) rate,
(int) 1000,
burst
);
h->value.data = b->pos;
h->value.len = b->last - b->pos;
}

if (1) {
ngx_table_elt_t *h;

h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}

h->hash = 1;
h->key = (ngx_str_t) ngx_string("x-ratelimit-remaining");

ngx_buf_t *b = ngx_create_temp_buf(r->pool, 256);
if (b == NULL) {
return NGX_ERROR;
}
b->last = ngx_sprintf(b->last, "%d, excess=%ui;burst=%ui;remaining=%ui",
ngx_min(limit_r, limit),
excess,
burst,
min_remaining
);
h->value.data = b->pos;
h->value.len = b->last - b->pos;

}

if (1) {
ngx_table_elt_t *h;

h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}

h->hash = 1;
h->key = (ngx_str_t) ngx_string("x-ratelimit-reset");

ngx_buf_t *b = ngx_create_temp_buf(r->pool, 256);
if (b == NULL) {
return NGX_ERROR;
}
b->last = ngx_sprintf(b->last, "%d",
(int) (rate < 1000 ? 1000 / rate: 1)
);
h->value.data = b->pos;
h->value.len = b->last - b->pos;

}
}
/** end header addition */


if (rc == NGX_DECLINED) {
return NGX_DECLINED;
Expand All @@ -237,11 +344,14 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
if (rc == NGX_BUSY || rc == NGX_ERROR) {

if (rc == NGX_BUSY) {

ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
"limiting requests%s, excess: %ui.%03ui by zone \"%V\"",
"limiting requests%s, excess: %ui.%03ui rem=%d by zone \"%V\" ",
lrcf->dry_run ? ", dry run" : "",
excess / 1000, excess % 1000,
min_remaining,
&limit->shm_zone->shm.name);

}

while (n--) {
Expand Down Expand Up @@ -376,7 +486,7 @@ ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,

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)
ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t *remaining, ngx_uint_t account)
{
size_t size;
ngx_int_t rc, excess;
Expand Down Expand Up @@ -424,10 +534,16 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
ms = 0;
}

/**
* The last `1000` is the request weight.
*/
excess = lr->excess - ctx->rate * ms / 1000 + 1000;

if (excess < 0) {
*remaining = -excess;
excess = 0;
} else {
*remaining = 0;
}

*ep = excess;
Expand Down Expand Up @@ -752,7 +868,7 @@ ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
NGX_LOG_INFO : conf->limit_log_level + 1;

ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
NGX_HTTP_SERVICE_UNAVAILABLE);
NGX_HTTP_TOO_MANY_REQUESTS);

ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);

Expand Down