Mercurial > hg > nginx
annotate src/stream/ngx_stream_upstream_least_conn_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 | 2cd019520210 |
children | 2f41d383c9c7 |
rev | line source |
---|---|
6115 | 1 |
2 /* | |
3 * Copyright (C) Maxim Dounin | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_stream.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_stream_upstream_init_least_conn_peer( | |
14 ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us); | |
15 static ngx_int_t ngx_stream_upstream_get_least_conn_peer( | |
16 ngx_peer_connection_t *pc, void *data); | |
17 static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, | |
18 void *conf); | |
19 | |
20 | |
21 static ngx_command_t ngx_stream_upstream_least_conn_commands[] = { | |
22 | |
23 { ngx_string("least_conn"), | |
24 NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS, | |
25 ngx_stream_upstream_least_conn, | |
26 0, | |
27 0, | |
28 NULL }, | |
29 | |
30 ngx_null_command | |
31 }; | |
32 | |
33 | |
34 static ngx_stream_module_t ngx_stream_upstream_least_conn_module_ctx = { | |
6174
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6115
diff
changeset
|
35 NULL, /* postconfiguration */ |
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6115
diff
changeset
|
36 |
6115 | 37 NULL, /* create main configuration */ |
38 NULL, /* init main configuration */ | |
39 | |
40 NULL, /* create server configuration */ | |
41 NULL, /* merge server configuration */ | |
42 }; | |
43 | |
44 | |
45 ngx_module_t ngx_stream_upstream_least_conn_module = { | |
46 NGX_MODULE_V1, | |
47 &ngx_stream_upstream_least_conn_module_ctx, /* module context */ | |
48 ngx_stream_upstream_least_conn_commands, /* module directives */ | |
49 NGX_STREAM_MODULE, /* module type */ | |
50 NULL, /* init master */ | |
51 NULL, /* init module */ | |
52 NULL, /* init process */ | |
53 NULL, /* init thread */ | |
54 NULL, /* exit thread */ | |
55 NULL, /* exit process */ | |
56 NULL, /* exit master */ | |
57 NGX_MODULE_V1_PADDING | |
58 }; | |
59 | |
60 | |
61 static ngx_int_t | |
62 ngx_stream_upstream_init_least_conn(ngx_conf_t *cf, | |
63 ngx_stream_upstream_srv_conf_t *us) | |
64 { | |
65 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, | |
66 "init least conn"); | |
67 | |
68 if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { | |
69 return NGX_ERROR; | |
70 } | |
71 | |
72 us->peer.init = ngx_stream_upstream_init_least_conn_peer; | |
73 | |
74 return NGX_OK; | |
75 } | |
76 | |
77 | |
78 static ngx_int_t | |
79 ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s, | |
80 ngx_stream_upstream_srv_conf_t *us) | |
81 { | |
82 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, | |
83 "init least conn peer"); | |
84 | |
85 if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { | |
86 return NGX_ERROR; | |
87 } | |
88 | |
89 s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer; | |
90 | |
91 return NGX_OK; | |
92 } | |
93 | |
94 | |
95 static ngx_int_t | |
96 ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) | |
97 { | |
98 ngx_stream_upstream_rr_peer_data_t *rrp = data; | |
99 | |
100 time_t now; | |
101 uintptr_t m; | |
102 ngx_int_t rc, total; | |
103 ngx_uint_t i, n, p, many; | |
104 ngx_stream_upstream_rr_peer_t *peer, *best; | |
105 ngx_stream_upstream_rr_peers_t *peers; | |
106 | |
107 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
108 "get least conn peer, try: %ui", pc->tries); | |
109 | |
110 if (rrp->peers->single) { | |
111 return ngx_stream_upstream_get_round_robin_peer(pc, rrp); | |
112 } | |
113 | |
114 pc->connection = NULL; | |
115 | |
116 now = ngx_time(); | |
117 | |
118 peers = rrp->peers; | |
119 | |
120 ngx_stream_upstream_rr_peers_wlock(peers); | |
121 | |
122 best = NULL; | |
123 total = 0; | |
124 | |
125 #if (NGX_SUPPRESS_WARN) | |
126 many = 0; | |
127 p = 0; | |
128 #endif | |
129 | |
130 for (peer = peers->peer, i = 0; | |
131 peer; | |
132 peer = peer->next, i++) | |
133 { | |
134 | |
135 n = i / (8 * sizeof(uintptr_t)); | |
136 m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); | |
137 | |
138 if (rrp->tried[n] & m) { | |
139 continue; | |
140 } | |
141 | |
142 if (peer->down) { | |
143 continue; | |
144 } | |
145 | |
146 if (peer->max_fails | |
147 && peer->fails >= peer->max_fails | |
148 && now - peer->checked <= peer->fail_timeout) | |
149 { | |
150 continue; | |
151 } | |
152 | |
153 /* | |
154 * select peer with least number of connections; if there are | |
155 * multiple peers with the same number of connections, select | |
156 * based on round-robin | |
157 */ | |
158 | |
159 if (best == NULL | |
160 || peer->conns * best->weight < best->conns * peer->weight) | |
161 { | |
162 best = peer; | |
163 many = 0; | |
164 p = i; | |
165 | |
166 } else if (peer->conns * best->weight == best->conns * peer->weight) { | |
167 many = 1; | |
168 } | |
169 } | |
170 | |
171 if (best == NULL) { | |
172 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
173 "get least conn peer, no peer found"); | |
174 | |
175 goto failed; | |
176 } | |
177 | |
178 if (many) { | |
179 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
180 "get least conn peer, many"); | |
181 | |
182 for (peer = best, i = p; | |
183 peer; | |
184 peer = peer->next, i++) | |
185 { | |
186 n = i / (8 * sizeof(uintptr_t)); | |
187 m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); | |
188 | |
189 if (rrp->tried[n] & m) { | |
190 continue; | |
191 } | |
192 | |
193 if (peer->down) { | |
194 continue; | |
195 } | |
196 | |
197 if (peer->conns * best->weight != best->conns * peer->weight) { | |
198 continue; | |
199 } | |
200 | |
201 if (peer->max_fails | |
202 && peer->fails >= peer->max_fails | |
203 && now - peer->checked <= peer->fail_timeout) | |
204 { | |
205 continue; | |
206 } | |
207 | |
208 peer->current_weight += peer->effective_weight; | |
209 total += peer->effective_weight; | |
210 | |
211 if (peer->effective_weight < peer->weight) { | |
212 peer->effective_weight++; | |
213 } | |
214 | |
215 if (peer->current_weight > best->current_weight) { | |
216 best = peer; | |
217 p = i; | |
218 } | |
219 } | |
220 } | |
221 | |
222 best->current_weight -= total; | |
223 | |
224 if (now - best->checked > best->fail_timeout) { | |
225 best->checked = now; | |
226 } | |
227 | |
228 pc->sockaddr = best->sockaddr; | |
229 pc->socklen = best->socklen; | |
230 pc->name = &best->name; | |
231 | |
232 best->conns++; | |
233 | |
234 rrp->current = best; | |
235 | |
236 n = p / (8 * sizeof(uintptr_t)); | |
237 m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); | |
238 | |
239 rrp->tried[n] |= m; | |
240 | |
241 ngx_stream_upstream_rr_peers_unlock(peers); | |
242 | |
243 return NGX_OK; | |
244 | |
245 failed: | |
246 | |
247 if (peers->next) { | |
248 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
249 "get least conn peer, backup servers"); | |
250 | |
251 rrp->peers = peers->next; | |
252 | |
253 n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) | |
254 / (8 * sizeof(uintptr_t)); | |
255 | |
256 for (i = 0; i < n; i++) { | |
6474 | 257 rrp->tried[i] = 0; |
6115 | 258 } |
259 | |
260 ngx_stream_upstream_rr_peers_unlock(peers); | |
261 | |
262 rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp); | |
263 | |
264 if (rc != NGX_BUSY) { | |
265 return rc; | |
266 } | |
267 | |
268 ngx_stream_upstream_rr_peers_wlock(peers); | |
269 } | |
270 | |
271 /* all peers failed, mark them as live for quick recovery */ | |
272 | |
273 for (peer = peers->peer; peer; peer = peer->next) { | |
274 peer->fails = 0; | |
275 } | |
276 | |
277 ngx_stream_upstream_rr_peers_unlock(peers); | |
278 | |
279 pc->name = peers->name; | |
280 | |
281 return NGX_BUSY; | |
282 } | |
283 | |
284 | |
285 static char * | |
286 ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
287 { | |
288 ngx_stream_upstream_srv_conf_t *uscf; | |
289 | |
290 uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); | |
291 | |
292 if (uscf->peer.init_upstream) { | |
293 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
294 "load balancing method redefined"); | |
295 } | |
296 | |
297 uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn; | |
298 | |
299 uscf->flags = NGX_STREAM_UPSTREAM_CREATE | |
300 |NGX_STREAM_UPSTREAM_WEIGHT | |
301 |NGX_STREAM_UPSTREAM_MAX_FAILS | |
302 |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT | |
303 |NGX_STREAM_UPSTREAM_DOWN | |
304 |NGX_STREAM_UPSTREAM_BACKUP; | |
305 | |
306 return NGX_CONF_OK; | |
307 } |