Mercurial > hg > nginx
annotate src/http/modules/ngx_http_split_clients_module.c @ 4247:b79dbadb3d5e stable-1.0
Merging r4147, r4148, r4149, r4150, r4207:
Fixes of combination of error_page and return directives:
*) Fix for incorrect 201 replies from dav module.
Replies with 201 code contain body, and we should clearly indicate it's
empty if it's empty. Before 0.8.32 chunked was explicitly disabled for
201 replies and as a result empty body was indicated by connection close
(not perfect, but worked). Since 0.8.32 chunked is enabled, and this
causes incorrect responses from dav module when HTTP/1.1 is used: with
"Transfer-Encoding: chunked" but no chunks at all.
Fix is to actually return empty body in special response handler instead
of abusing r->header_only flag.
See here for initial report:
http://mailman.nginx.org/pipermail/nginx-ru/2010-October/037535.html
*) Fix for double content when return is used in error_page handler.
Test case:
location / {
error_page 405 /nope;
return 405;
}
location /nope {
return 200;
}
This is expected to return 405 with empty body, but in 0.8.42+ will return
builtin 405 error page as well (though not counted in Content-Length, thus
breaking protocol).
Fix is to use status provided by rewrite script execution in case
it's less than NGX_HTTP_BAD_REQUEST even if r->error_status set. This
check is in line with one in ngx_http_script_return_code().
Note that this patch also changes behaviour for "return 302 ..." and
"rewrite ... redirect" used as error handler. E.g.
location / {
error_page 405 /redirect;
return 405;
}
location /redirect {
rewrite ^ http://example.com/;
}
will actually return redirect to "http://example.com/" instead of builtin
405 error page with meaningless Location header. This looks like correct
change and it's in line with what happens on e.g. directory redirects
in error handlers.
*) Fix for "return 202" not discarding body.
Big POST (not fully preread) to a
location / {
return 202;
}
resulted in incorrect behaviour due to "return" code path not calling
ngx_http_discard_request_body(). The same applies to all "return" used
with 2xx/3xx codes except 201 and 204, and to all "return ... text" uses.
Fix is to add ngx_http_discard_request_body() call to
ngx_http_send_response() function where it looks appropriate.
Discard body call from emtpy gif module removed as it's now redundant.
Reported by Pyry Hakulinen, see
http://mailman.nginx.org/pipermail/nginx/2011-August/028503.html
*) Incorrect special case for "return 204" removed.
The special case in question leads to replies without body in
configuration like
location / { error_page 404 /zero; return 404; }
location /zero { return 204; }
while replies with empty body are expected per protocol specs.
Correct one will look like
if (status == NGX_HTTP_NO_CONTENT) {
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || r->header_only) {
return rc;
}
return ngx_http_send_special(r, NGX_HTTP_LAST);
}
though it looks like it's better to drop this special case at all.
*) Clear old Location header (if any) while adding a new one.
This prevents incorrect behaviour when another redirect is issued within
error_page 302 handler.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 01 Nov 2011 13:45:33 +0000 |
parents | eccd0b66a4ab |
children | d620f497c50f |
rev | line source |
---|---|
3513 | 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 uint32_t percent; | |
14 ngx_http_variable_value_t value; | |
15 } ngx_http_split_clients_part_t; | |
16 | |
17 | |
18 typedef struct { | |
19 ngx_http_complex_value_t value; | |
20 ngx_array_t parts; | |
21 } ngx_http_split_clients_ctx_t; | |
22 | |
23 | |
24 static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
25 void *conf); | |
26 static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, | |
27 void *conf); | |
28 | |
29 static ngx_command_t ngx_http_split_clients_commands[] = { | |
30 | |
31 { ngx_string("split_clients"), | |
32 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
33 ngx_conf_split_clients_block, | |
34 NGX_HTTP_MAIN_CONF_OFFSET, | |
35 0, | |
36 NULL }, | |
37 | |
38 ngx_null_command | |
39 }; | |
40 | |
41 | |
42 static ngx_http_module_t ngx_http_split_clients_module_ctx = { | |
43 NULL, /* preconfiguration */ | |
44 NULL, /* postconfiguration */ | |
45 | |
46 NULL, /* create main configuration */ | |
47 NULL, /* init main configuration */ | |
48 | |
49 NULL, /* create server configuration */ | |
50 NULL, /* merge server configuration */ | |
51 | |
52 NULL, /* create location configuration */ | |
53 NULL /* merge location configuration */ | |
54 }; | |
55 | |
56 | |
57 ngx_module_t ngx_http_split_clients_module = { | |
58 NGX_MODULE_V1, | |
59 &ngx_http_split_clients_module_ctx, /* module context */ | |
60 ngx_http_split_clients_commands, /* module directives */ | |
61 NGX_HTTP_MODULE, /* module type */ | |
62 NULL, /* init master */ | |
63 NULL, /* init module */ | |
64 NULL, /* init process */ | |
65 NULL, /* init thread */ | |
66 NULL, /* exit thread */ | |
67 NULL, /* exit process */ | |
68 NULL, /* exit master */ | |
69 NGX_MODULE_V1_PADDING | |
70 }; | |
71 | |
72 | |
73 static ngx_int_t | |
74 ngx_http_split_clients_variable(ngx_http_request_t *r, | |
75 ngx_http_variable_value_t *v, uintptr_t data) | |
76 { | |
77 ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data; | |
78 | |
79 uint32_t hash; | |
80 ngx_str_t val; | |
81 ngx_uint_t i; | |
82 ngx_http_split_clients_part_t *part; | |
83 | |
84 *v = ngx_http_variable_null_value; | |
85 | |
86 if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { | |
87 return NGX_OK; | |
88 } | |
89 | |
3892
12d8d2f30205
use MurmurHash2 for split_clients, because
Igor Sysoev <igor@sysoev.ru>
parents:
3607
diff
changeset
|
90 hash = ngx_murmur_hash2(val.data, val.len); |
3513 | 91 |
92 part = ctx->parts.elts; | |
93 | |
94 for (i = 0; i < ctx->parts.nelts; i++) { | |
95 | |
96 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3918 | 97 "http split: %uD %uD", hash, part[i].percent); |
3513 | 98 |
99 if (hash < part[i].percent) { | |
100 *v = part[i].value; | |
101 return NGX_OK; | |
102 } | |
103 } | |
104 | |
105 return NGX_OK; | |
106 } | |
107 | |
108 | |
109 static char * | |
110 ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
111 { | |
112 char *rv; | |
113 ngx_str_t *value, name; | |
114 ngx_uint_t i, sum, last; | |
115 ngx_conf_t save; | |
116 ngx_http_variable_t *var; | |
117 ngx_http_split_clients_ctx_t *ctx; | |
118 ngx_http_split_clients_part_t *part; | |
119 ngx_http_compile_complex_value_t ccv; | |
120 | |
121 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t)); | |
122 if (ctx == NULL) { | |
123 return NGX_CONF_ERROR; | |
124 } | |
125 | |
126 value = cf->args->elts; | |
127 | |
128 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | |
129 | |
130 ccv.cf = cf; | |
131 ccv.value = &value[1]; | |
132 ccv.complex_value = &ctx->value; | |
133 | |
134 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | |
135 return NGX_CONF_ERROR; | |
136 } | |
137 | |
138 name = value[2]; | |
139 name.len--; | |
140 name.data++; | |
141 | |
142 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); | |
143 if (var == NULL) { | |
144 return NGX_CONF_ERROR; | |
145 } | |
146 | |
147 var->get_handler = ngx_http_split_clients_variable; | |
148 var->data = (uintptr_t) ctx; | |
149 | |
150 if (ngx_array_init(&ctx->parts, cf->pool, 2, | |
151 sizeof(ngx_http_split_clients_part_t)) | |
152 != NGX_OK) | |
153 { | |
154 return NGX_CONF_ERROR; | |
155 } | |
156 | |
157 save = *cf; | |
158 cf->ctx = ctx; | |
159 cf->handler = ngx_http_split_clients; | |
160 cf->handler_conf = conf; | |
161 | |
162 rv = ngx_conf_parse(cf, NULL); | |
163 | |
164 *cf = save; | |
165 | |
3607
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
166 if (rv != NGX_CONF_OK) { |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
167 return rv; |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
168 } |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
169 |
3513 | 170 sum = 0; |
171 last = 0; | |
172 part = ctx->parts.elts; | |
173 | |
174 for (i = 0; i < ctx->parts.nelts; i++) { | |
175 sum = part[i].percent ? sum + part[i].percent : 10000; | |
176 if (sum > 10000) { | |
177 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
178 "percent sum is more than 100%%"); | |
179 return NGX_CONF_ERROR; | |
180 } | |
181 | |
182 if (part[i].percent) { | |
183 part[i].percent = (uint32_t) | |
184 (last + 0xffffffff / 10000 * part[i].percent); | |
185 } else { | |
186 part[i].percent = 0xffffffff; | |
187 } | |
188 | |
189 last = part[i].percent; | |
190 } | |
191 | |
192 return rv; | |
193 } | |
194 | |
195 | |
196 static char * | |
197 ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
198 { | |
199 ngx_int_t n; | |
200 ngx_str_t *value; | |
201 ngx_http_split_clients_ctx_t *ctx; | |
202 ngx_http_split_clients_part_t *part; | |
203 | |
204 ctx = cf->ctx; | |
205 value = cf->args->elts; | |
206 | |
207 part = ngx_array_push(&ctx->parts); | |
208 if (part == NULL) { | |
209 return NGX_CONF_ERROR; | |
210 } | |
211 | |
212 if (value[0].len == 1 && value[0].data[0] == '*') { | |
213 part->percent = 0; | |
214 | |
215 } else { | |
216 if (value[0].data[value[0].len - 1] != '%') { | |
217 goto invalid; | |
218 } | |
219 | |
220 n = ngx_atofp(value[0].data, value[0].len - 1, 2); | |
221 if (n == NGX_ERROR || n == 0) { | |
222 goto invalid; | |
223 } | |
224 | |
225 part->percent = (uint32_t) n; | |
226 } | |
227 | |
228 part->value.len = value[1].len; | |
229 part->value.valid = 1; | |
230 part->value.no_cacheable = 0; | |
231 part->value.not_found = 0; | |
232 part->value.data = value[1].data; | |
233 | |
234 return NGX_CONF_OK; | |
235 | |
236 invalid: | |
237 | |
238 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
239 "invalid percent value \"%V\"", &value[0]); | |
240 return NGX_CONF_ERROR; | |
241 } |