Mercurial > hg > nginx
annotate src/http/modules/ngx_http_random_index_module.c @ 6749:f88a145b093e stable-1.10
HTTP/2: the "421 Misdirected Request" response (closes #848).
Since 4fbef397c753 nginx rejects with the 400 error any attempts of
requesting different host over the same connection, if the relevant
virtual server requires verification of a client certificate.
While requesting hosts other than negotiated isn't something legal
in HTTP/1.x, the HTTP/2 specification explicitly permits such requests
for connection reuse and has introduced a special response code 421.
According to RFC 7540 Section 9.1.2 this code can be sent by a server
that is not configured to produce responses for the combination of
scheme and authority that are included in the request URI. And the
client may retry the request over a different connection.
Now this code is used for requests that aren't authorized in current
connection. After receiving the 421 response a client will be able
to open a new connection, provide the required certificate and retry
the request.
Unfortunately, not all clients currently are able to handle it well.
Notably Chrome just shows an error, while at least the latest version
of Firefox retries the request over a new connection.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Fri, 20 May 2016 18:41:17 +0300 |
parents | 47daf95d0138 |
children | 061ec464813f |
rev | line source |
---|---|
2235 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4412 | 4 * Copyright (C) Nginx, Inc. |
2235 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 ngx_flag_t enable; | |
15 } ngx_http_random_index_loc_conf_t; | |
16 | |
17 | |
18 #define NGX_HTTP_RANDOM_INDEX_PREALLOCATE 50 | |
19 | |
20 | |
21 static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r, | |
22 ngx_dir_t *dir, ngx_str_t *name); | |
2266
6223d5a9e87f
back out $random_index variable
Igor Sysoev <igor@sysoev.ru>
parents:
2258
diff
changeset
|
23 static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf); |
2235 | 24 static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf); |
25 static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, | |
26 void *parent, void *child); | |
27 | |
28 | |
29 static ngx_command_t ngx_http_random_index_commands[] = { | |
30 | |
31 { ngx_string("random_index"), | |
32 NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
33 ngx_conf_set_flag_slot, | |
34 NGX_HTTP_LOC_CONF_OFFSET, | |
35 offsetof(ngx_http_random_index_loc_conf_t, enable), | |
36 NULL }, | |
37 | |
38 ngx_null_command | |
39 }; | |
40 | |
41 | |
42 static ngx_http_module_t ngx_http_random_index_module_ctx = { | |
2266
6223d5a9e87f
back out $random_index variable
Igor Sysoev <igor@sysoev.ru>
parents:
2258
diff
changeset
|
43 NULL, /* preconfiguration */ |
2235 | 44 ngx_http_random_index_init, /* postconfiguration */ |
45 | |
46 NULL, /* create main configuration */ | |
47 NULL, /* init main configuration */ | |
48 | |
49 NULL, /* create server configuration */ | |
50 NULL, /* merge server configuration */ | |
51 | |
4499
778ef9c3fd2d
Fixed spelling in single-line comments.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
52 ngx_http_random_index_create_loc_conf, /* create location configuration */ |
778ef9c3fd2d
Fixed spelling in single-line comments.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
53 ngx_http_random_index_merge_loc_conf /* merge location configuration */ |
2235 | 54 }; |
55 | |
56 | |
57 ngx_module_t ngx_http_random_index_module = { | |
58 NGX_MODULE_V1, | |
59 &ngx_http_random_index_module_ctx, /* module context */ | |
60 ngx_http_random_index_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_random_index_handler(ngx_http_request_t *r) | |
75 { | |
76 u_char *last, *filename; | |
77 size_t len, allocated, root; | |
78 ngx_err_t err; | |
79 ngx_int_t rc; | |
80 ngx_str_t path, uri, *name; | |
81 ngx_dir_t dir; | |
82 ngx_uint_t n, level; | |
83 ngx_array_t names; | |
84 ngx_http_random_index_loc_conf_t *rlcf; | |
85 | |
86 if (r->uri.data[r->uri.len - 1] != '/') { | |
87 return NGX_DECLINED; | |
88 } | |
89 | |
90 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { | |
91 return NGX_DECLINED; | |
92 } | |
93 | |
94 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module); | |
95 | |
96 if (!rlcf->enable) { | |
97 return NGX_DECLINED; | |
98 } | |
99 | |
100 #if (NGX_HAVE_D_TYPE) | |
101 len = NGX_DIR_MASK_LEN; | |
102 #else | |
103 len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE; | |
104 #endif | |
105 | |
106 last = ngx_http_map_uri_to_path(r, &path, &root, len); | |
107 if (last == NULL) { | |
108 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
109 } | |
110 | |
111 allocated = path.len; | |
112 | |
113 path.len = last - path.data - 1; | |
114 path.data[path.len] = '\0'; | |
115 | |
116 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
117 "http random index: \"%s\"", path.data); | |
118 | |
119 if (ngx_open_dir(&path, &dir) == NGX_ERROR) { | |
120 err = ngx_errno; | |
121 | |
122 if (err == NGX_ENOENT | |
123 || err == NGX_ENOTDIR | |
124 || err == NGX_ENAMETOOLONG) | |
125 { | |
126 level = NGX_LOG_ERR; | |
127 rc = NGX_HTTP_NOT_FOUND; | |
128 | |
129 } else if (err == NGX_EACCES) { | |
130 level = NGX_LOG_ERR; | |
131 rc = NGX_HTTP_FORBIDDEN; | |
132 | |
133 } else { | |
134 level = NGX_LOG_CRIT; | |
135 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
136 } | |
137 | |
138 ngx_log_error(level, r->connection->log, err, | |
139 ngx_open_dir_n " \"%s\" failed", path.data); | |
140 | |
141 return rc; | |
142 } | |
143 | |
144 if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) { | |
145 return ngx_http_random_index_error(r, &dir, &path); | |
146 } | |
147 | |
148 filename = path.data; | |
149 filename[path.len] = '/'; | |
150 | |
151 for ( ;; ) { | |
152 ngx_set_errno(0); | |
153 | |
154 if (ngx_read_dir(&dir) == NGX_ERROR) { | |
155 err = ngx_errno; | |
156 | |
157 if (err != NGX_ENOMOREFILES) { | |
158 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
159 ngx_read_dir_n " \"%V\" failed", &path); | |
160 return ngx_http_random_index_error(r, &dir, &path); | |
161 } | |
162 | |
163 break; | |
164 } | |
165 | |
166 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
167 "http random index file: \"%s\"", ngx_de_name(&dir)); | |
168 | |
169 if (ngx_de_name(&dir)[0] == '.') { | |
170 continue; | |
171 } | |
172 | |
173 len = ngx_de_namelen(&dir); | |
174 | |
3203
a56cccd588e6
update r3201: ngx_http_random_index_module should behave consistently
Igor Sysoev <igor@sysoev.ru>
parents:
3200
diff
changeset
|
175 if (dir.type == 0 || ngx_de_is_link(&dir)) { |
2235 | 176 |
177 /* 1 byte for '/' and 1 byte for terminating '\0' */ | |
178 | |
179 if (path.len + 1 + len + 1 > allocated) { | |
180 allocated = path.len + 1 + len + 1 | |
181 + NGX_HTTP_RANDOM_INDEX_PREALLOCATE; | |
182 | |
183 filename = ngx_pnalloc(r->pool, allocated); | |
184 if (filename == NULL) { | |
185 return ngx_http_random_index_error(r, &dir, &path); | |
186 } | |
187 | |
188 last = ngx_cpystrn(filename, path.data, path.len + 1); | |
189 *last++ = '/'; | |
190 } | |
191 | |
192 ngx_cpystrn(last, ngx_de_name(&dir), len + 1); | |
193 | |
194 if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) { | |
195 err = ngx_errno; | |
196 | |
197 if (err != NGX_ENOENT) { | |
198 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
199 ngx_de_info_n " \"%s\" failed", filename); | |
200 return ngx_http_random_index_error(r, &dir, &path); | |
201 } | |
202 | |
203 if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) { | |
204 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
205 ngx_de_link_info_n " \"%s\" failed", | |
206 filename); | |
207 return ngx_http_random_index_error(r, &dir, &path); | |
208 } | |
209 } | |
210 } | |
211 | |
212 if (!ngx_de_is_file(&dir)) { | |
213 continue; | |
214 } | |
215 | |
216 name = ngx_array_push(&names); | |
217 if (name == NULL) { | |
218 return ngx_http_random_index_error(r, &dir, &path); | |
219 } | |
220 | |
221 name->len = len; | |
222 | |
223 name->data = ngx_pnalloc(r->pool, len); | |
224 if (name->data == NULL) { | |
225 return ngx_http_random_index_error(r, &dir, &path); | |
226 } | |
227 | |
228 ngx_memcpy(name->data, ngx_de_name(&dir), len); | |
229 } | |
230 | |
231 if (ngx_close_dir(&dir) == NGX_ERROR) { | |
232 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
6477
47daf95d0138
Fixed logging in close error handling.
Sergey Kandaurov <pluknet@nginx.com>
parents:
4499
diff
changeset
|
233 ngx_close_dir_n " \"%V\" failed", &path); |
2235 | 234 } |
235 | |
236 n = names.nelts; | |
237 | |
238 if (n == 0) { | |
239 return NGX_DECLINED; | |
240 } | |
241 | |
242 name = names.elts; | |
243 | |
244 n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000); | |
245 | |
246 uri.len = r->uri.len + name[n].len; | |
247 | |
248 uri.data = ngx_pnalloc(r->pool, uri.len); | |
249 if (uri.data == NULL) { | |
250 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
251 } | |
252 | |
253 last = ngx_copy(uri.data, r->uri.data, r->uri.len); | |
254 ngx_memcpy(last, name[n].data, name[n].len); | |
255 | |
256 return ngx_http_internal_redirect(r, &uri, &r->args); | |
257 } | |
258 | |
259 | |
260 static ngx_int_t | |
261 ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir, | |
262 ngx_str_t *name) | |
263 { | |
264 if (ngx_close_dir(dir) == NGX_ERROR) { | |
265 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
266 ngx_close_dir_n " \"%V\" failed", name); | |
267 } | |
268 | |
269 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
270 } | |
271 | |
272 | |
273 static void * | |
274 ngx_http_random_index_create_loc_conf(ngx_conf_t *cf) | |
275 { | |
276 ngx_http_random_index_loc_conf_t *conf; | |
277 | |
278 conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t)); | |
279 if (conf == NULL) { | |
2912
c7d57b539248
return NULL instead of NGX_CONF_ERROR on a create conf failure
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
280 return NULL; |
2235 | 281 } |
282 | |
283 conf->enable = NGX_CONF_UNSET; | |
284 | |
285 return conf; | |
286 } | |
287 | |
288 | |
289 static char * | |
290 ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
291 { | |
292 ngx_http_random_index_loc_conf_t *prev = parent; | |
293 ngx_http_random_index_loc_conf_t *conf = child; | |
294 | |
295 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
296 | |
297 return NGX_CONF_OK; | |
298 } | |
299 | |
300 | |
301 static ngx_int_t | |
302 ngx_http_random_index_init(ngx_conf_t *cf) | |
303 { | |
304 ngx_http_handler_pt *h; | |
305 ngx_http_core_main_conf_t *cmcf; | |
306 | |
307 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
308 | |
309 h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); | |
310 if (h == NULL) { | |
311 return NGX_ERROR; | |
312 } | |
313 | |
314 *h = ngx_http_random_index_handler; | |
315 | |
316 return NGX_OK; | |
317 } |