Ejemplo n.º 1
0
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;

    r->connection->write->delayed = 1;
    ngx_add_timer(r->connection->write, delay);

    return NGX_AGAIN;
}
Ejemplo n.º 2
0
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    size_t                         n, total_len;
    uint32_t                       hash;
    ngx_int_t                      rc;
    ngx_msec_t                     delay_time;
    ngx_uint_t                     excess, delay_excess, delay_postion,
                                   nodelay, i;
    ngx_time_t                    *tp;
    ngx_rbtree_node_t             *node;
    ngx_http_limit_req_t          *limit_req;
    ngx_http_limit_req_ctx_t      *ctx;
    ngx_http_limit_req_node_t     *lr;
    ngx_http_limit_req_conf_t     *lrcf;

    delay_excess = 0;
    delay_postion = 0;
    nodelay = 0;
    ctx = NULL;
    rc = 0;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
    if (lrcf->rules == NULL) {
        return NGX_DECLINED;
    }

    if (!lrcf->enable) {
        return NGX_DECLINED;
    }

    /* filter whitelist */
    if (ngx_http_limit_req_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }

    /* to match limit_req rule*/
    limit_req = lrcf->rules->elts;
    for (i = 0; i < lrcf->rules->nelts; i++) {
        ctx = limit_req[i].shm_zone->data;

        ngx_crc32_init(hash);
        total_len = 0;

        total_len = ngx_http_limit_req_copy_variables(r, &hash, ctx, NULL);
        if (total_len == 0) {
            continue;
        }

        ngx_crc32_final(hash);

        r->main->limit_req_set = 1;

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

        ngx_http_limit_req_expire(r, ctx, 1);
        rc = ngx_http_limit_req_lookup(r, &limit_req[i], hash, &excess);
        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limit_req module: %i %ui.%03ui "
                       "hash is %ui total_len is %i",
                       rc, excess / 1000, excess % 1000, hash, total_len);

        /* first limit_req */
        if (rc == NGX_DECLINED) {

            n = offsetof(ngx_rbtree_node_t, color)
                + offsetof(ngx_http_limit_req_node_t, data)
                + total_len;

            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node == NULL) {
                ngx_http_limit_req_expire(r, ctx, 0);
                node = ngx_slab_alloc_locked(ctx->shpool, n);
                if (node == NULL) {
                    ngx_shmtx_unlock(&ctx->shpool->mutex);
                    return NGX_HTTP_SERVICE_UNAVAILABLE;
                }
            }

            lr = (ngx_http_limit_req_node_t *) &node->color;

            node->key = hash;
            lr->len = (u_char) total_len;

            tp = ngx_timeofday();
            lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

            lr->excess = 0;
            ngx_http_limit_req_copy_variables(r, &hash, ctx, lr);

            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
            ngx_rbtree_insert(&ctx->sh->rbtree, node);

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

            continue;
        }

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

        if (rc == NGX_OK) {
            continue;
        }

        /* need limit request */
        if (rc == NGX_BUSY) {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limit_req limiting requests, "
                          "excess: %ui.%03ui by zone \"%V\"",
                          excess / 1000, excess % 1000,
                         &limit_req[i].shm_zone->shm.name);

            if (limit_req[i].forbid_action.len == 0) {

                return NGX_HTTP_SERVICE_UNAVAILABLE;
            } else if (limit_req[i].forbid_action.data[0] == '@') {

                ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                              "limiting requests, forbid_action is %V",
                              &limit_req[i].forbid_action);
                (void) ngx_http_named_location(r, &limit_req[i].forbid_action);

            } else {

                ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                              "limiting requests, forbid_action is %V",
                              &limit_req[i].forbid_action);
                (void) ngx_http_internal_redirect(r,
                                                  &limit_req[i].forbid_action,
                                                  &r->args);
            }

            ngx_http_finalize_request(r, NGX_DONE);
            return NGX_DONE;

        }

        if (rc == NGX_AGAIN) {
            if (delay_excess < excess) {
                delay_excess = excess;
                nodelay = limit_req[i].nodelay;
                delay_postion = i;
            }
        }
    }

    if (rc == 0) {
        return NGX_DECLINED;
    }

    /* rc = NGX_AGAIN */
    if (delay_excess != 0) {

        if (nodelay) {
            return NGX_DECLINED;
        }

        delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate;
        ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                      "delaying request,"
                      "excess: %ui.%03ui, by zone \"%V\", delay \"%ui\" s",
                      delay_excess / 1000, delay_excess % 1000,
                      &limit_req[delay_postion].shm_zone->shm.name, delay_time);

        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_time);

        return NGX_AGAIN;
    }

    /* rc == NGX_OK or rc == NGX_DECLINED */

    return NGX_DECLINED;
}
Ejemplo n.º 3
0
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    size_t                      len, n;
    uint32_t                    hash;
    ngx_int_t                   rc;
    ngx_uint_t                  excess;
    ngx_time_t                 *tp;
    ngx_rbtree_node_t          *node;
    ngx_http_variable_value_t  *vv;
    ngx_http_limit_req_ctx_t   *ctx;
    ngx_http_limit_req_node_t  *lr;
    ngx_http_limit_req_conf_t  *lrcf;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);

    if (lrcf->shm_zone == NULL) {
        return NGX_DECLINED;
    }

    ctx = lrcf->shm_zone->data;

    vv = ngx_http_get_indexed_variable(r, ctx->index);

    if (vv == NULL || vv->not_found) {
        return NGX_DECLINED;
    }

    len = vv->len;

    if (len == 0) {
        return NGX_DECLINED;
    }

    if (len > 65535) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "the value of the \"%V\" variable "
                      "is more than 65535 bytes: \"%v\"",
                      &ctx->var, vv);
        return NGX_DECLINED;
    }

    r->main->limit_req_set = 1;

    hash = ngx_crc32_short(vv->data, len);

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

    ngx_http_limit_req_expire(ctx, 1);

    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);

    if (lr) {
        ngx_queue_remove(&lr->queue);

        ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

        excess = lr->excess;

    } else {
        excess = 0;
    }

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                  "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);

    if (rc == NGX_BUSY) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                      excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);

        return NGX_HTTP_SERVICE_UNAVAILABLE;
    }

    if (rc == NGX_AGAIN) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);

        if (lrcf->nodelay) {
            return NGX_DECLINED;
        }

        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                      "delaying request, excess: %ui.%03ui, by zone \"%V\"",
                      excess / 1000, excess % 1000, &lrcf->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, (ngx_msec_t) excess);

        return NGX_AGAIN;
    }

    if (rc == NGX_OK) {
        goto done;
    }

    /* rc == NGX_DECLINED */

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_http_limit_req_node_t, data)
        + len;

    node = ngx_slab_alloc_locked(ctx->shpool, n);
    if (node == NULL) {

        ngx_http_limit_req_expire(ctx, 0);

        node = ngx_slab_alloc_locked(ctx->shpool, n);
        if (node == NULL) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            return NGX_HTTP_SERVICE_UNAVAILABLE;
        }
    }

    lr = (ngx_http_limit_req_node_t *) &node->color;

    node->key = hash;
    lr->len = (u_char) len;

    tp = ngx_timeofday();
    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

    lr->excess = 0;
    ngx_memcpy(lr->data, vv->data, len);

    ngx_rbtree_insert(&ctx->sh->rbtree, node);

    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

done:

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

    return NGX_DECLINED;
}
Ejemplo n.º 4
0
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    size_t                       len;
    uint32_t                     hash;
    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;

    /* filter whitelist */
    if (ngx_http_limit_req_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }

    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;

        ngx_crc32_init(hash);

        len = ngx_http_limit_req_copy_variables(r, &hash, ctx, NULL);
        if (len == 0) {
            continue;
        }

        ngx_crc32_final(hash);

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

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

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

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limits[%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;
        }

        if (rc == NGX_ERROR || limit->forbid_action.len == 0) {
            return lrcf->status_code;
        } else if (limit->forbid_action.data[0] == '@') {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_named_location(r, &limit->forbid_action);

        } else {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_internal_redirect(r,
                                             &limit->forbid_action,
                                             &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
    }

    /* 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;
}