~ubuntu-branches/ubuntu/karmic/nginx/karmic

« back to all changes in this revision

Viewing changes to src/http/modules/ngx_http_limit_req_module.c

  • Committer: Bazaar Package Importer
  • Author(s): Fabio Tranchitella
  • Date: 2009-05-31 18:38:56 UTC
  • mfrom: (1.1.11 upstream) (4.2.2 sid)
  • Revision ID: james.westby@ubuntu.com-20090531183856-lkdgdzr9m731fz92
Tags: 0.7.59-1
* New upstream release, first in Debian for the 0.7 branch. Among other
  issues, it also fixes the problem with wildcard dns names used with SSL.
  (Closes: #515904)
* debian/watch: updated.
* debian/postinst: fixed a bashism. (Closes: #507913)
* debian/conf/nginx.conf: removed default_type. (Closes: #509390)
* debian/control: updated Standards-Version to 3.8.1, no changes needed.
* debian/NEWS.Debian: documented the issues with
  server_names_hash_bucket_size. (Closes: #524785)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
3
 * Copyright (C) Igor Sysoev
 
4
 */
 
5
 
 
6
 
 
7
#include <ngx_config.h>
 
8
#include <ngx_core.h>
 
9
#include <ngx_http.h>
 
10
 
 
11
 
 
12
typedef struct {
 
13
    u_char                       color;
 
14
    u_char                       dummy;
 
15
    u_short                      len;
 
16
    ngx_queue_t                  queue;
 
17
    ngx_msec_t                   last;
 
18
    /* integer value, 1 corresponds to 0.001 r/s */
 
19
    ngx_uint_t                   excess;
 
20
    u_char                       data[1];
 
21
} ngx_http_limit_req_node_t;
 
22
 
 
23
 
 
24
typedef struct {
 
25
    ngx_rbtree_t                  rbtree;
 
26
    ngx_rbtree_node_t             sentinel;
 
27
    ngx_queue_t                   queue;
 
28
} ngx_http_limit_req_shctx_t;
 
29
 
 
30
 
 
31
typedef struct {
 
32
    ngx_http_limit_req_shctx_t  *sh;
 
33
    ngx_slab_pool_t             *shpool;
 
34
    /* integer value, 1 corresponds to 0.001 r/s */
 
35
    ngx_uint_t                   rate;
 
36
    ngx_int_t                    index;
 
37
    ngx_str_t                    var;
 
38
} ngx_http_limit_req_ctx_t;
 
39
 
 
40
 
 
41
typedef struct {
 
42
    ngx_shm_zone_t              *shm_zone;
 
43
    /* integer value, 1 corresponds to 0.001 r/s */
 
44
    ngx_uint_t                   burst;
 
45
    ngx_uint_t                   nodelay;/* unsigned  nodelay:1 */
 
46
} ngx_http_limit_req_conf_t;
 
47
 
 
48
 
 
49
static void ngx_http_limit_req_delay(ngx_http_request_t *r);
 
50
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf,
 
51
    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp);
 
52
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
 
53
    ngx_uint_t n);
 
54
 
 
55
static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
 
56
static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
 
57
    void *child);
 
58
static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
 
59
    void *conf);
 
60
static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
 
61
    void *conf);
 
62
static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
 
63
 
 
64
 
 
65
static ngx_command_t  ngx_http_limit_req_commands[] = {
 
66
 
 
67
    { ngx_string("limit_req_zone"),
 
68
      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
 
69
      ngx_http_limit_req_zone,
 
70
      0,
 
71
      0,
 
72
      NULL },
 
73
 
 
74
    { ngx_string("limit_req"),
 
75
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
 
76
      ngx_http_limit_req,
 
77
      NGX_HTTP_LOC_CONF_OFFSET,
 
78
      0,
 
79
      NULL },
 
80
 
 
81
      ngx_null_command
 
82
};
 
83
 
 
84
 
 
85
static ngx_http_module_t  ngx_http_limit_req_module_ctx = {
 
86
    NULL,                                  /* preconfiguration */
 
87
    ngx_http_limit_req_init,               /* postconfiguration */
 
88
 
 
89
    NULL,                                  /* create main configuration */
 
90
    NULL,                                  /* init main configuration */
 
91
 
 
92
    NULL,                                  /* create server configuration */
 
93
    NULL,                                  /* merge server configuration */
 
94
 
 
95
    ngx_http_limit_req_create_conf,        /* create location configration */
 
96
    ngx_http_limit_req_merge_conf          /* merge location configration */
 
97
};
 
98
 
 
99
 
 
100
ngx_module_t  ngx_http_limit_req_module = {
 
101
    NGX_MODULE_V1,
 
102
    &ngx_http_limit_req_module_ctx,        /* module context */
 
103
    ngx_http_limit_req_commands,           /* module directives */
 
104
    NGX_HTTP_MODULE,                       /* module type */
 
105
    NULL,                                  /* init master */
 
106
    NULL,                                  /* init module */
 
107
    NULL,                                  /* init process */
 
108
    NULL,                                  /* init thread */
 
109
    NULL,                                  /* exit thread */
 
110
    NULL,                                  /* exit process */
 
111
    NULL,                                  /* exit master */
 
112
    NGX_MODULE_V1_PADDING
 
113
};
 
114
 
 
115
 
 
116
static ngx_int_t
 
117
ngx_http_limit_req_handler(ngx_http_request_t *r)
 
118
{
 
119
    size_t                      len, n;
 
120
    uint32_t                    hash;
 
121
    ngx_int_t                   rc;
 
122
    ngx_uint_t                  excess;
 
123
    ngx_time_t                 *tp;
 
124
    ngx_rbtree_node_t          *node;
 
125
    ngx_http_variable_value_t  *vv;
 
126
    ngx_http_limit_req_ctx_t   *ctx;
 
127
    ngx_http_limit_req_node_t  *lr;
 
128
    ngx_http_limit_req_conf_t  *lrcf;
 
129
 
 
130
    if (r->main->limit_req_set) {
 
131
        return NGX_DECLINED;
 
132
    }
 
133
 
 
134
    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
 
135
 
 
136
    if (lrcf->shm_zone == NULL) {
 
137
        return NGX_DECLINED;
 
138
    }
 
139
 
 
140
    ctx = lrcf->shm_zone->data;
 
141
 
 
142
    vv = ngx_http_get_indexed_variable(r, ctx->index);
 
143
 
 
144
    if (vv == NULL || vv->not_found) {
 
145
        return NGX_DECLINED;
 
146
    }
 
147
 
 
148
    len = vv->len;
 
149
 
 
150
    if (len == 0) {
 
151
        return NGX_DECLINED;
 
152
    }
 
153
 
 
154
    if (len > 65535) {
 
155
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
 
156
                      "the value of the \"%V\" variable "
 
157
                      "is more than 65535 bytes: \"%v\"",
 
158
                      &ctx->var, vv);
 
159
        return NGX_DECLINED;
 
160
    }
 
161
 
 
162
    r->main->limit_req_set = 1;
 
163
 
 
164
    hash = ngx_crc32_short(vv->data, len);
 
165
 
 
166
    ngx_shmtx_lock(&ctx->shpool->mutex);
 
167
 
 
168
    ngx_http_limit_req_expire(ctx, 1);
 
169
 
 
170
    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);
 
171
 
 
172
    if (lr) {
 
173
        ngx_queue_remove(&lr->queue);
 
174
 
 
175
        ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
 
176
 
 
177
        excess = lr->excess;
 
178
 
 
179
    } else {
 
180
        excess = 0;
 
181
    }
 
182
 
 
183
    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
 
184
                  "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
 
185
 
 
186
    if (rc == NGX_BUSY) {
 
187
        ngx_shmtx_unlock(&ctx->shpool->mutex);
 
188
 
 
189
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
 
190
                      "limiting requests, excess: %ui.%03ui by zone \"%V\"",
 
191
                      excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
 
192
 
 
193
        return NGX_HTTP_SERVICE_UNAVAILABLE;
 
194
    }
 
195
 
 
196
    if (rc == NGX_AGAIN) {
 
197
        ngx_shmtx_unlock(&ctx->shpool->mutex);
 
198
 
 
199
        if (lrcf->nodelay) {
 
200
            return NGX_DECLINED;
 
201
        }
 
202
 
 
203
        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
 
204
                      "delaying request, excess: %ui.%03ui, by zone \"%V\"",
 
205
                      excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
 
206
 
 
207
        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
 
208
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
 
209
        }
 
210
 
 
211
        r->read_event_handler = ngx_http_test_reading;
 
212
        r->write_event_handler = ngx_http_limit_req_delay;
 
213
        ngx_add_timer(r->connection->write, (ngx_msec_t) excess);
 
214
 
 
215
        return NGX_AGAIN;
 
216
    }
 
217
 
 
218
    if (rc == NGX_OK) {
 
219
        goto done;
 
220
    }
 
221
 
 
222
    /* rc == NGX_DECLINED */
 
223
 
 
224
    n = offsetof(ngx_rbtree_node_t, color)
 
225
        + offsetof(ngx_http_limit_req_node_t, data)
 
226
        + len;
 
227
 
 
228
    node = ngx_slab_alloc_locked(ctx->shpool, n);
 
229
    if (node == NULL) {
 
230
 
 
231
        ngx_http_limit_req_expire(ctx, 0);
 
232
 
 
233
        node = ngx_slab_alloc_locked(ctx->shpool, n);
 
234
        if (node == NULL) {
 
235
            ngx_shmtx_unlock(&ctx->shpool->mutex);
 
236
            return NGX_HTTP_SERVICE_UNAVAILABLE;
 
237
        }
 
238
    }
 
239
 
 
240
    lr = (ngx_http_limit_req_node_t *) &node->color;
 
241
 
 
242
    node->key = hash;
 
243
    lr->len = (u_char) len;
 
244
 
 
245
    tp = ngx_timeofday();
 
246
    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
 
247
 
 
248
    lr->excess = 0;
 
249
    ngx_memcpy(lr->data, vv->data, len);
 
250
 
 
251
    ngx_rbtree_insert(&ctx->sh->rbtree, node);
 
252
 
 
253
    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
 
254
 
 
255
done:
 
256
 
 
257
    ngx_shmtx_unlock(&ctx->shpool->mutex);
 
258
 
 
259
    return NGX_DECLINED;
 
260
}
 
261
 
 
262
 
 
263
static void
 
264
ngx_http_limit_req_delay(ngx_http_request_t *r)
 
265
{
 
266
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
 
267
                  "limit_req delay");
 
268
 
 
269
    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
 
270
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
 
271
        return;
 
272
    }
 
273
 
 
274
    r->read_event_handler = ngx_http_block_reading;
 
275
    r->write_event_handler = ngx_http_core_run_phases;
 
276
 
 
277
    ngx_http_core_run_phases(r);
 
278
}
 
279
 
 
280
 
 
281
static void
 
282
ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
 
283
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 
284
{
 
285
    ngx_rbtree_node_t          **p;
 
286
    ngx_http_limit_req_node_t   *lrn, *lrnt;
 
287
 
 
288
    for ( ;; ) {
 
289
 
 
290
        if (node->key < temp->key) {
 
291
 
 
292
            p = &temp->left;
 
293
 
 
294
        } else if (node->key > temp->key) {
 
295
 
 
296
            p = &temp->right;
 
297
 
 
298
        } else { /* node->key == temp->key */
 
299
 
 
300
            lrn = (ngx_http_limit_req_node_t *) &node->color;
 
301
            lrnt = (ngx_http_limit_req_node_t *) &temp->color;
 
302
 
 
303
            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
 
304
                ? &temp->left : &temp->right;
 
305
        }
 
306
 
 
307
        if (*p == sentinel) {
 
308
            break;
 
309
        }
 
310
 
 
311
        temp = *p;
 
312
    }
 
313
 
 
314
    *p = node;
 
315
    node->parent = temp;
 
316
    node->left = sentinel;
 
317
    node->right = sentinel;
 
318
    ngx_rbt_red(node);
 
319
}
 
320
 
 
321
 
 
322
static ngx_int_t
 
323
ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash,
 
324
    u_char *data, size_t len, ngx_http_limit_req_node_t **lrp)
 
325
{
 
326
    ngx_int_t                   rc, excess;
 
327
    ngx_time_t                 *tp;
 
328
    ngx_msec_t                  now;
 
329
    ngx_msec_int_t              ms;
 
330
    ngx_rbtree_node_t          *node, *sentinel;
 
331
    ngx_http_limit_req_ctx_t   *ctx;
 
332
    ngx_http_limit_req_node_t  *lr;
 
333
 
 
334
    ctx = lrcf->shm_zone->data;
 
335
 
 
336
    node = ctx->sh->rbtree.root;
 
337
    sentinel = ctx->sh->rbtree.sentinel;
 
338
 
 
339
    while (node != sentinel) {
 
340
 
 
341
        if (hash < node->key) {
 
342
            node = node->left;
 
343
            continue;
 
344
        }
 
345
 
 
346
        if (hash > node->key) {
 
347
            node = node->right;
 
348
            continue;
 
349
        }
 
350
 
 
351
        /* hash == node->key */
 
352
 
 
353
        do {
 
354
            lr = (ngx_http_limit_req_node_t *) &node->color;
 
355
 
 
356
            rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
 
357
 
 
358
            if (rc == 0) {
 
359
 
 
360
                tp = ngx_timeofday();
 
361
 
 
362
                now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
 
363
                ms = (ngx_msec_int_t) (now - lr->last);
 
364
 
 
365
                excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
 
366
 
 
367
                if (excess < 0) {
 
368
                    excess = 0;
 
369
                }
 
370
 
 
371
                lr->excess = excess;
 
372
                lr->last = now;
 
373
 
 
374
                *lrp = lr;
 
375
 
 
376
                if ((ngx_uint_t) excess > lrcf->burst) {
 
377
                    return NGX_BUSY;
 
378
                }
 
379
 
 
380
                if (excess) {
 
381
                    return NGX_AGAIN;
 
382
                }
 
383
 
 
384
                return NGX_OK;
 
385
            }
 
386
 
 
387
            node = (rc < 0) ? node->left : node->right;
 
388
 
 
389
        } while (node != sentinel && hash == node->key);
 
390
 
 
391
        break;
 
392
    }
 
393
 
 
394
    *lrp = NULL;
 
395
 
 
396
    return NGX_DECLINED;
 
397
}
 
398
 
 
399
 
 
400
static void
 
401
ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
 
402
{
 
403
    ngx_int_t                   excess;
 
404
    ngx_time_t                 *tp;
 
405
    ngx_msec_t                  now;
 
406
    ngx_queue_t                *q;
 
407
    ngx_msec_int_t              ms;
 
408
    ngx_rbtree_node_t          *node;
 
409
    ngx_http_limit_req_node_t  *lr;
 
410
 
 
411
    tp = ngx_timeofday();
 
412
 
 
413
    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
 
414
 
 
415
    /*
 
416
     * n == 1 deletes one or two zero rate entries
 
417
     * n == 0 deletes oldest entry by force
 
418
     *        and one or two zero rate entries
 
419
     */
 
420
 
 
421
    while (n < 3) {
 
422
 
 
423
        if (ngx_queue_empty(&ctx->sh->queue)) {
 
424
            return;
 
425
        }
 
426
 
 
427
        q = ngx_queue_last(&ctx->sh->queue);
 
428
 
 
429
        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
 
430
 
 
431
        if (n++ != 0) {
 
432
 
 
433
            ms = (ngx_msec_int_t) (now - lr->last);
 
434
            ms = ngx_abs(ms);
 
435
 
 
436
            if (ms < 60000) {
 
437
                return;
 
438
            }
 
439
 
 
440
            excess = lr->excess - ctx->rate * ms / 1000;
 
441
 
 
442
            if (excess > 0) {
 
443
                return;
 
444
            }
 
445
        }
 
446
 
 
447
        ngx_queue_remove(q);
 
448
 
 
449
        node = (ngx_rbtree_node_t *)
 
450
                   ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
 
451
 
 
452
        ngx_rbtree_delete(&ctx->sh->rbtree, node);
 
453
 
 
454
        ngx_slab_free_locked(ctx->shpool, node);
 
455
    }
 
456
}
 
457
 
 
458
 
 
459
static ngx_int_t
 
460
ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
 
461
{
 
462
    ngx_http_limit_req_ctx_t  *octx = data;
 
463
 
 
464
    size_t                     len;
 
465
    ngx_http_limit_req_ctx_t  *ctx;
 
466
 
 
467
    ctx = shm_zone->data;
 
468
 
 
469
    if (octx) {
 
470
        if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
 
471
            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
 
472
                          "limit_req \"%V\" uses the \"%V\" variable "
 
473
                          "while previously it used the \"%V\" variable",
 
474
                          &shm_zone->shm.name, &ctx->var, &octx->var);
 
475
            return NGX_ERROR;
 
476
        }
 
477
 
 
478
        ctx->sh = octx->sh;
 
479
        ctx->shpool = octx->shpool;
 
480
 
 
481
        return NGX_OK;
 
482
    }
 
483
 
 
484
    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
 
485
 
 
486
    if (shm_zone->shm.exists) {
 
487
        ctx->sh = ctx->shpool->data;
 
488
 
 
489
        return NGX_OK;
 
490
    }
 
491
 
 
492
    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
 
493
    if (ctx->sh == NULL) {
 
494
        return NGX_ERROR;
 
495
    }
 
496
 
 
497
    ctx->shpool->data = ctx->sh;
 
498
 
 
499
    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
 
500
                    ngx_http_limit_req_rbtree_insert_value);
 
501
 
 
502
    ngx_queue_init(&ctx->sh->queue);
 
503
 
 
504
    len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
 
505
 
 
506
    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
 
507
    if (ctx->shpool->log_ctx == NULL) {
 
508
        return NGX_ERROR;
 
509
    }
 
510
 
 
511
    ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
 
512
                &shm_zone->shm.name);
 
513
 
 
514
    return NGX_OK;
 
515
}
 
516
 
 
517
 
 
518
static void *
 
519
ngx_http_limit_req_create_conf(ngx_conf_t *cf)
 
520
{
 
521
    ngx_http_limit_req_conf_t  *conf;
 
522
 
 
523
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
 
524
    if (conf == NULL) {
 
525
        return NGX_CONF_ERROR;
 
526
    }
 
527
 
 
528
    /*
 
529
     * set by ngx_pcalloc():
 
530
     *
 
531
     *     conf->shm_zone = NULL;
 
532
     *     conf->burst = 0;
 
533
     *     conf->nodelay = 0;
 
534
     */
 
535
 
 
536
    return conf;
 
537
}
 
538
 
 
539
 
 
540
static char *
 
541
ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 
542
{
 
543
    ngx_http_limit_req_conf_t *prev = parent;
 
544
    ngx_http_limit_req_conf_t *conf = child;
 
545
 
 
546
    if (conf->shm_zone == NULL) {
 
547
        *conf = *prev;
 
548
    }
 
549
 
 
550
    return NGX_CONF_OK;
 
551
}
 
552
 
 
553
 
 
554
static char *
 
555
ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
556
{
 
557
    u_char                    *p;
 
558
    size_t                     size, len;
 
559
    ngx_str_t                 *value, name, s;
 
560
    ngx_int_t                  rate, scale;
 
561
    ngx_uint_t                 i;
 
562
    ngx_shm_zone_t            *shm_zone;
 
563
    ngx_http_limit_req_ctx_t  *ctx;
 
564
 
 
565
    value = cf->args->elts;
 
566
 
 
567
    ctx = NULL;
 
568
    size = 0;
 
569
    rate = 1;
 
570
    scale = 1;
 
571
    name.len = 0;
 
572
 
 
573
    for (i = 1; i < cf->args->nelts; i++) {
 
574
 
 
575
        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
 
576
 
 
577
            name.data = value[i].data + 5;
 
578
 
 
579
            p = (u_char *) ngx_strchr(name.data, ':');
 
580
 
 
581
            if (p) {
 
582
                *p = '\0';
 
583
 
 
584
                name.len = p - name.data;
 
585
 
 
586
                p++;
 
587
 
 
588
                s.len = value[i].data + value[i].len - p;
 
589
                s.data = p;
 
590
 
 
591
                size = ngx_parse_size(&s);
 
592
                if (size > 8191) {
 
593
                    continue;
 
594
                }
 
595
            }
 
596
 
 
597
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
598
                               "invalid zone size \"%V\"", &value[i]);
 
599
            return NGX_CONF_ERROR;
 
600
        }
 
601
 
 
602
        if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
 
603
 
 
604
            len = value[i].len;
 
605
            p = value[i].data + len - 3;
 
606
 
 
607
            if (ngx_strncmp(p, "r/s", 3) == 0) {
 
608
                scale = 1;
 
609
                len -= 3;
 
610
 
 
611
            } else if (ngx_strncmp(p, "r/m", 3) == 0) {
 
612
                scale = 60;
 
613
                len -= 3;
 
614
            }
 
615
 
 
616
            rate = ngx_atoi(value[i].data + 5, len - 5);
 
617
            if (rate <= NGX_ERROR) {
 
618
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
619
                                   "invalid rate \"%V\"", &value[i]);
 
620
                return NGX_CONF_ERROR;
 
621
            }
 
622
 
 
623
            continue;
 
624
        }
 
625
 
 
626
        if (value[i].data[0] == '$') {
 
627
 
 
628
            value[i].len--;
 
629
            value[i].data++;
 
630
 
 
631
            ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
 
632
            if (ctx == NULL) {
 
633
                return NGX_CONF_ERROR;
 
634
            }
 
635
 
 
636
            ctx->index = ngx_http_get_variable_index(cf, &value[i]);
 
637
            if (ctx->index == NGX_ERROR) {
 
638
                return NGX_CONF_ERROR;
 
639
            }
 
640
 
 
641
            ctx->var = value[i];
 
642
 
 
643
            continue;
 
644
        }
 
645
 
 
646
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
647
                           "invalid parameter \"%V\"", &value[i]);
 
648
        return NGX_CONF_ERROR;
 
649
    }
 
650
 
 
651
    if (name.len == 0 || size == 0) {
 
652
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
653
                           "\"%V\" must have \"zone\" parameter",
 
654
                           &cmd->name);
 
655
        return NGX_CONF_ERROR;
 
656
    }
 
657
 
 
658
    if (ctx == NULL) {
 
659
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
660
                           "no variable is defined for limit_req_zone \"%V\"",
 
661
                           &cmd->name);
 
662
        return NGX_CONF_ERROR;
 
663
    }
 
664
 
 
665
    ctx->rate = rate * 1000 / scale;
 
666
 
 
667
    shm_zone = ngx_shared_memory_add(cf, &name, size,
 
668
                                     &ngx_http_limit_req_module);
 
669
    if (shm_zone == NULL) {
 
670
        return NGX_CONF_ERROR;
 
671
    }
 
672
 
 
673
    if (shm_zone->data) {
 
674
        ctx = shm_zone->data;
 
675
 
 
676
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
677
                   "limit_req_zone \"%V\" is already bound to variable \"%V\"",
 
678
                   &value[1], &ctx->var);
 
679
        return NGX_CONF_ERROR;
 
680
    }
 
681
 
 
682
    shm_zone->init = ngx_http_limit_req_init_zone;
 
683
    shm_zone->data = ctx;
 
684
 
 
685
    return NGX_CONF_OK;
 
686
}
 
687
 
 
688
 
 
689
static char *
 
690
ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
691
{
 
692
    ngx_http_limit_req_conf_t  *lrcf = conf;
 
693
 
 
694
    ngx_int_t    burst;
 
695
    ngx_str_t   *value, s;
 
696
    ngx_uint_t   i;
 
697
 
 
698
    if (lrcf->shm_zone) {
 
699
        return "is duplicate";
 
700
    }
 
701
 
 
702
    value = cf->args->elts;
 
703
 
 
704
    burst = 0;
 
705
 
 
706
    for (i = 1; i < cf->args->nelts; i++) {
 
707
 
 
708
        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
 
709
 
 
710
            s.len = value[i].len - 5;
 
711
            s.data = value[i].data + 5;
 
712
 
 
713
            lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0,
 
714
                                                   &ngx_http_limit_req_module);
 
715
            if (lrcf->shm_zone == NULL) {
 
716
                return NGX_CONF_ERROR;
 
717
            }
 
718
 
 
719
            continue;
 
720
        }
 
721
 
 
722
        if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
 
723
 
 
724
            burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
 
725
            if (burst <= 0) {
 
726
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
727
                                   "invalid burst rate \"%V\"", &value[i]);
 
728
                return NGX_CONF_ERROR;
 
729
            }
 
730
 
 
731
            continue;
 
732
        }
 
733
 
 
734
        if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
 
735
            lrcf->nodelay = 1;
 
736
            continue;
 
737
        }
 
738
 
 
739
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
740
                           "invalid parameter \"%V\"", &value[i]);
 
741
        return NGX_CONF_ERROR;
 
742
    }
 
743
 
 
744
    if (lrcf->shm_zone == NULL) {
 
745
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
746
                           "\"%V\" must have \"zone\" parameter",
 
747
                           &cmd->name);
 
748
        return NGX_CONF_ERROR;
 
749
    }
 
750
 
 
751
    if (lrcf->shm_zone->data == NULL) {
 
752
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
 
753
                           "unknown limit_req_zone \"%V\"",
 
754
                           &lrcf->shm_zone->shm.name);
 
755
        return NGX_CONF_ERROR;
 
756
    }
 
757
 
 
758
    lrcf->burst = burst * 1000;
 
759
 
 
760
    return NGX_CONF_OK;
 
761
}
 
762
 
 
763
 
 
764
static ngx_int_t
 
765
ngx_http_limit_req_init(ngx_conf_t *cf)
 
766
{
 
767
    ngx_http_handler_pt        *h;
 
768
    ngx_http_core_main_conf_t  *cmcf;
 
769
 
 
770
    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
 
771
 
 
772
    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
 
773
    if (h == NULL) {
 
774
        return NGX_ERROR;
 
775
    }
 
776
 
 
777
    *h = ngx_http_limit_req_handler;
 
778
 
 
779
    return NGX_OK;
 
780
}