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;