Mercurial > hg > nginx
comparison src/http/modules/ngx_http_limit_req_module.c @ 5862:ecbb99aa0e12
Limit req: use complex value in limit_req_zone.
One intentional side effect of this change is that key is allowed only
in the first position. Previously, it was possible to specify the key
variable at any position, but that was never documented, and is contrary
with nginx configuration practice for positional parameters.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Wed, 24 Sep 2014 21:55:19 +0400 |
parents | cda4fcb9294c |
children | 102f85699420 |
comparison
equal
deleted
inserted
replaced
5861:1e6bf87a7289 | 5862:ecbb99aa0e12 |
---|---|
33 typedef struct { | 33 typedef struct { |
34 ngx_http_limit_req_shctx_t *sh; | 34 ngx_http_limit_req_shctx_t *sh; |
35 ngx_slab_pool_t *shpool; | 35 ngx_slab_pool_t *shpool; |
36 /* integer value, 1 corresponds to 0.001 r/s */ | 36 /* integer value, 1 corresponds to 0.001 r/s */ |
37 ngx_uint_t rate; | 37 ngx_uint_t rate; |
38 ngx_int_t index; | 38 ngx_http_complex_value_t key; |
39 ngx_str_t var; | |
40 ngx_http_limit_req_node_t *node; | 39 ngx_http_limit_req_node_t *node; |
41 } ngx_http_limit_req_ctx_t; | 40 } ngx_http_limit_req_ctx_t; |
42 | 41 |
43 | 42 |
44 typedef struct { | 43 typedef struct { |
156 | 155 |
157 | 156 |
158 static ngx_int_t | 157 static ngx_int_t |
159 ngx_http_limit_req_handler(ngx_http_request_t *r) | 158 ngx_http_limit_req_handler(ngx_http_request_t *r) |
160 { | 159 { |
161 size_t len; | |
162 uint32_t hash; | 160 uint32_t hash; |
161 ngx_str_t key; | |
163 ngx_int_t rc; | 162 ngx_int_t rc; |
164 ngx_uint_t n, excess; | 163 ngx_uint_t n, excess; |
165 ngx_msec_t delay; | 164 ngx_msec_t delay; |
166 ngx_http_variable_value_t *vv; | |
167 ngx_http_limit_req_ctx_t *ctx; | 165 ngx_http_limit_req_ctx_t *ctx; |
168 ngx_http_limit_req_conf_t *lrcf; | 166 ngx_http_limit_req_conf_t *lrcf; |
169 ngx_http_limit_req_limit_t *limit, *limits; | 167 ngx_http_limit_req_limit_t *limit, *limits; |
170 | 168 |
171 if (r->main->limit_req_set) { | 169 if (r->main->limit_req_set) { |
187 | 185 |
188 limit = &limits[n]; | 186 limit = &limits[n]; |
189 | 187 |
190 ctx = limit->shm_zone->data; | 188 ctx = limit->shm_zone->data; |
191 | 189 |
192 vv = ngx_http_get_indexed_variable(r, ctx->index); | 190 if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { |
193 | 191 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
194 if (vv == NULL || vv->not_found) { | 192 } |
195 continue; | 193 |
196 } | 194 if (key.len == 0) { |
197 | 195 continue; |
198 len = vv->len; | 196 } |
199 | 197 |
200 if (len == 0) { | 198 if (key.len > 65535) { |
201 continue; | |
202 } | |
203 | |
204 if (len > 65535) { | |
205 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 199 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
206 "the value of the \"%V\" variable " | 200 "the value of the \"%V\" key " |
207 "is more than 65535 bytes: \"%v\"", | 201 "is more than 65535 bytes: \"%V\"", |
208 &ctx->var, vv); | 202 &ctx->key.value, &key); |
209 continue; | 203 continue; |
210 } | 204 } |
211 | 205 |
212 hash = ngx_crc32_short(vv->data, len); | 206 hash = ngx_crc32_short(key.data, key.len); |
213 | 207 |
214 ngx_shmtx_lock(&ctx->shpool->mutex); | 208 ngx_shmtx_lock(&ctx->shpool->mutex); |
215 | 209 |
216 rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess, | 210 rc = ngx_http_limit_req_lookup(limit, hash, key.data, key.len, &excess, |
217 (n == lrcf->limits.nelts - 1)); | 211 (n == lrcf->limits.nelts - 1)); |
218 | 212 |
219 ngx_shmtx_unlock(&ctx->shpool->mutex); | 213 ngx_shmtx_unlock(&ctx->shpool->mutex); |
220 | 214 |
221 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 215 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
630 ngx_http_limit_req_ctx_t *ctx; | 624 ngx_http_limit_req_ctx_t *ctx; |
631 | 625 |
632 ctx = shm_zone->data; | 626 ctx = shm_zone->data; |
633 | 627 |
634 if (octx) { | 628 if (octx) { |
635 if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { | 629 if (ctx->key.value.len != octx->key.value.len |
630 || ngx_strncmp(ctx->key.value.data, octx->key.value.data, | |
631 ctx->key.value.len) | |
632 != 0) | |
633 { | |
636 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, | 634 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, |
637 "limit_req \"%V\" uses the \"%V\" variable " | 635 "limit_req \"%V\" uses the \"%V\" key " |
638 "while previously it used the \"%V\" variable", | 636 "while previously it used the \"%V\" key", |
639 &shm_zone->shm.name, &ctx->var, &octx->var); | 637 &shm_zone->shm.name, &ctx->key.value, |
638 &octx->key.value); | |
640 return NGX_ERROR; | 639 return NGX_ERROR; |
641 } | 640 } |
642 | 641 |
643 ctx->sh = octx->sh; | 642 ctx->sh = octx->sh; |
644 ctx->shpool = octx->shpool; | 643 ctx->shpool = octx->shpool; |
729 | 728 |
730 | 729 |
731 static char * | 730 static char * |
732 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 731 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
733 { | 732 { |
734 u_char *p; | 733 u_char *p; |
735 size_t len; | 734 size_t len; |
736 ssize_t size; | 735 ssize_t size; |
737 ngx_str_t *value, name, s; | 736 ngx_str_t *value, name, s; |
738 ngx_int_t rate, scale; | 737 ngx_int_t rate, scale; |
739 ngx_uint_t i; | 738 ngx_uint_t i; |
740 ngx_shm_zone_t *shm_zone; | 739 ngx_shm_zone_t *shm_zone; |
741 ngx_http_limit_req_ctx_t *ctx; | 740 ngx_http_limit_req_ctx_t *ctx; |
741 ngx_http_compile_complex_value_t ccv; | |
742 | 742 |
743 value = cf->args->elts; | 743 value = cf->args->elts; |
744 | 744 |
745 ctx = NULL; | 745 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); |
746 if (ctx == NULL) { | |
747 return NGX_CONF_ERROR; | |
748 } | |
749 | |
750 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | |
751 | |
752 ccv.cf = cf; | |
753 ccv.value = &value[1]; | |
754 ccv.complex_value = &ctx->key; | |
755 | |
756 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | |
757 return NGX_CONF_ERROR; | |
758 } | |
759 | |
746 size = 0; | 760 size = 0; |
747 rate = 1; | 761 rate = 1; |
748 scale = 1; | 762 scale = 1; |
749 name.len = 0; | 763 name.len = 0; |
750 | 764 |
751 for (i = 1; i < cf->args->nelts; i++) { | 765 for (i = 2; i < cf->args->nelts; i++) { |
752 | 766 |
753 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { | 767 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { |
754 | 768 |
755 name.data = value[i].data + 5; | 769 name.data = value[i].data + 5; |
756 | 770 |
806 } | 820 } |
807 | 821 |
808 continue; | 822 continue; |
809 } | 823 } |
810 | 824 |
811 if (value[i].data[0] == '$') { | |
812 | |
813 value[i].len--; | |
814 value[i].data++; | |
815 | |
816 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); | |
817 if (ctx == NULL) { | |
818 return NGX_CONF_ERROR; | |
819 } | |
820 | |
821 ctx->index = ngx_http_get_variable_index(cf, &value[i]); | |
822 if (ctx->index == NGX_ERROR) { | |
823 return NGX_CONF_ERROR; | |
824 } | |
825 | |
826 ctx->var = value[i]; | |
827 | |
828 continue; | |
829 } | |
830 | |
831 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 825 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
832 "invalid parameter \"%V\"", &value[i]); | 826 "invalid parameter \"%V\"", &value[i]); |
833 return NGX_CONF_ERROR; | 827 return NGX_CONF_ERROR; |
834 } | 828 } |
835 | 829 |
838 "\"%V\" must have \"zone\" parameter", | 832 "\"%V\" must have \"zone\" parameter", |
839 &cmd->name); | 833 &cmd->name); |
840 return NGX_CONF_ERROR; | 834 return NGX_CONF_ERROR; |
841 } | 835 } |
842 | 836 |
843 if (ctx == NULL) { | |
844 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
845 "no variable is defined for %V \"%V\"", | |
846 &cmd->name, &name); | |
847 return NGX_CONF_ERROR; | |
848 } | |
849 | |
850 ctx->rate = rate * 1000 / scale; | 837 ctx->rate = rate * 1000 / scale; |
851 | 838 |
852 shm_zone = ngx_shared_memory_add(cf, &name, size, | 839 shm_zone = ngx_shared_memory_add(cf, &name, size, |
853 &ngx_http_limit_req_module); | 840 &ngx_http_limit_req_module); |
854 if (shm_zone == NULL) { | 841 if (shm_zone == NULL) { |
857 | 844 |
858 if (shm_zone->data) { | 845 if (shm_zone->data) { |
859 ctx = shm_zone->data; | 846 ctx = shm_zone->data; |
860 | 847 |
861 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 848 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
862 "%V \"%V\" is already bound to variable \"%V\"", | 849 "%V \"%V\" is already bound to key \"%V\"", |
863 &cmd->name, &name, &ctx->var); | 850 &cmd->name, &name, &ctx->key.value); |
864 return NGX_CONF_ERROR; | 851 return NGX_CONF_ERROR; |
865 } | 852 } |
866 | 853 |
867 shm_zone->init = ngx_http_limit_req_init_zone; | 854 shm_zone->init = ngx_http_limit_req_init_zone; |
868 shm_zone->data = ctx; | 855 shm_zone->data = ctx; |