Mercurial > hg > nginx-quic
annotate src/stream/ngx_stream_handler.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 | a01e315b3a78 |
children | c70b7f4537e1 |
rev | line source |
---|---|
6115 | 1 |
2 /* | |
3 * Copyright (C) Roman Arutyunyan | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_event.h> | |
11 #include <ngx_stream.h> | |
12 | |
13 | |
14 static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len); | |
15 static void ngx_stream_init_session(ngx_connection_t *c); | |
16 | |
17 #if (NGX_STREAM_SSL) | |
18 static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); | |
19 static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); | |
20 #endif | |
21 | |
22 | |
23 void | |
24 ngx_stream_init_connection(ngx_connection_t *c) | |
25 { | |
6221
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
26 int tcp_nodelay; |
6175 | 27 u_char text[NGX_SOCKADDR_STRLEN]; |
28 size_t len; | |
29 ngx_int_t rc; | |
30 ngx_uint_t i; | |
31 struct sockaddr *sa; | |
32 ngx_stream_port_t *port; | |
33 struct sockaddr_in *sin; | |
34 ngx_stream_in_addr_t *addr; | |
35 ngx_stream_session_t *s; | |
36 ngx_stream_addr_conf_t *addr_conf; | |
6115 | 37 #if (NGX_HAVE_INET6) |
6175 | 38 struct sockaddr_in6 *sin6; |
39 ngx_stream_in6_addr_t *addr6; | |
6115 | 40 #endif |
6175 | 41 ngx_stream_core_srv_conf_t *cscf; |
42 ngx_stream_core_main_conf_t *cmcf; | |
6115 | 43 |
44 /* find the server configuration for the address:port */ | |
45 | |
46 port = c->listening->servers; | |
47 | |
48 if (port->naddrs > 1) { | |
49 | |
50 /* | |
51 * There are several addresses on this port and one of them | |
52 * is the "*:port" wildcard so getsockname() is needed to determine | |
53 * the server address. | |
54 * | |
6436 | 55 * AcceptEx() and recvmsg() already gave this address. |
6115 | 56 */ |
57 | |
58 if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { | |
59 ngx_stream_close_connection(c); | |
60 return; | |
61 } | |
62 | |
63 sa = c->local_sockaddr; | |
64 | |
65 switch (sa->sa_family) { | |
66 | |
67 #if (NGX_HAVE_INET6) | |
68 case AF_INET6: | |
69 sin6 = (struct sockaddr_in6 *) sa; | |
70 | |
71 addr6 = port->addrs; | |
72 | |
73 /* the last address is "*" */ | |
74 | |
75 for (i = 0; i < port->naddrs - 1; i++) { | |
76 if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { | |
77 break; | |
78 } | |
79 } | |
80 | |
81 addr_conf = &addr6[i].conf; | |
82 | |
83 break; | |
84 #endif | |
85 | |
86 default: /* AF_INET */ | |
87 sin = (struct sockaddr_in *) sa; | |
88 | |
89 addr = port->addrs; | |
90 | |
91 /* the last address is "*" */ | |
92 | |
93 for (i = 0; i < port->naddrs - 1; i++) { | |
94 if (addr[i].addr == sin->sin_addr.s_addr) { | |
95 break; | |
96 } | |
97 } | |
98 | |
99 addr_conf = &addr[i].conf; | |
100 | |
101 break; | |
102 } | |
103 | |
104 } else { | |
105 switch (c->local_sockaddr->sa_family) { | |
106 | |
107 #if (NGX_HAVE_INET6) | |
108 case AF_INET6: | |
109 addr6 = port->addrs; | |
110 addr_conf = &addr6[0].conf; | |
111 break; | |
112 #endif | |
113 | |
114 default: /* AF_INET */ | |
115 addr = port->addrs; | |
116 addr_conf = &addr[0].conf; | |
117 break; | |
118 } | |
119 } | |
120 | |
121 s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); | |
122 if (s == NULL) { | |
123 ngx_stream_close_connection(c); | |
124 return; | |
125 } | |
126 | |
127 s->signature = NGX_STREAM_MODULE; | |
128 s->main_conf = addr_conf->ctx->main_conf; | |
129 s->srv_conf = addr_conf->ctx->srv_conf; | |
130 | |
131 s->connection = c; | |
132 c->data = s; | |
133 | |
134 cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); | |
135 | |
6129
187aa751ad62
Core: the ngx_set_connection_log() macro.
Vladimir Homutov <vl@nginx.com>
parents:
6115
diff
changeset
|
136 ngx_set_connection_log(c, cscf->error_log); |
6115 | 137 |
138 len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); | |
139 | |
6461
a01e315b3a78
Stream: additional logging for UDP.
Vladimir Homutov <vl@nginx.com>
parents:
6436
diff
changeset
|
140 ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA %sclient %*s connected to %V", |
a01e315b3a78
Stream: additional logging for UDP.
Vladimir Homutov <vl@nginx.com>
parents:
6436
diff
changeset
|
141 c->number, c->type == SOCK_DGRAM ? "udp " : "", |
a01e315b3a78
Stream: additional logging for UDP.
Vladimir Homutov <vl@nginx.com>
parents:
6436
diff
changeset
|
142 len, text, &addr_conf->addr_text); |
6115 | 143 |
144 c->log->connection = c->number; | |
145 c->log->handler = ngx_stream_log_error; | |
146 c->log->data = s; | |
147 c->log->action = "initializing connection"; | |
148 c->log_error = NGX_ERROR_INFO; | |
149 | |
6175 | 150 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); |
151 | |
6197
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
152 if (cmcf->limit_conn_handler) { |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
153 rc = cmcf->limit_conn_handler(s); |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
154 |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
155 if (rc != NGX_DECLINED) { |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
156 ngx_stream_close_connection(c); |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
157 return; |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
158 } |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
159 } |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
160 |
6175 | 161 if (cmcf->access_handler) { |
162 rc = cmcf->access_handler(s); | |
163 | |
164 if (rc != NGX_OK && rc != NGX_DECLINED) { | |
165 ngx_stream_close_connection(c); | |
166 return; | |
167 } | |
168 } | |
169 | |
6436 | 170 if (c->type == SOCK_STREAM |
171 && cscf->tcp_nodelay | |
172 && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) | |
173 { | |
6221
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
174 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay"); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
175 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
176 tcp_nodelay = 1; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
177 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
178 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
179 (const void *) &tcp_nodelay, sizeof(int)) == -1) |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
180 { |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
181 ngx_connection_error(c, ngx_socket_errno, |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
182 "setsockopt(TCP_NODELAY) failed"); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
183 ngx_stream_close_connection(c); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
184 return; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
185 } |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
186 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
187 c->tcp_nodelay = NGX_TCP_NODELAY_SET; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
188 } |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
189 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
190 |
6115 | 191 #if (NGX_STREAM_SSL) |
192 { | |
193 ngx_stream_ssl_conf_t *sslcf; | |
194 | |
195 sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); | |
196 | |
197 if (addr_conf->ssl) { | |
198 c->log->action = "SSL handshaking"; | |
199 | |
200 if (sslcf->ssl.ctx == NULL) { | |
201 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
202 "no \"ssl_certificate\" is defined " | |
203 "in server listening on SSL port"); | |
204 ngx_stream_close_connection(c); | |
205 return; | |
206 } | |
207 | |
208 ngx_stream_ssl_init_connection(&sslcf->ssl, c); | |
209 return; | |
210 } | |
211 } | |
212 #endif | |
213 | |
214 ngx_stream_init_session(c); | |
215 } | |
216 | |
217 | |
218 static void | |
219 ngx_stream_init_session(ngx_connection_t *c) | |
220 { | |
221 ngx_stream_session_t *s; | |
222 ngx_stream_core_srv_conf_t *cscf; | |
223 | |
224 s = c->data; | |
225 c->log->action = "handling client connection"; | |
226 | |
227 cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); | |
228 | |
229 s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); | |
230 if (s->ctx == NULL) { | |
231 ngx_stream_close_connection(c); | |
232 return; | |
233 } | |
234 | |
235 cscf->handler(s); | |
236 } | |
237 | |
238 | |
239 #if (NGX_STREAM_SSL) | |
240 | |
241 static void | |
242 ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) | |
243 { | |
244 ngx_stream_session_t *s; | |
245 ngx_stream_ssl_conf_t *sslcf; | |
246 | |
247 if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { | |
248 ngx_stream_close_connection(c); | |
249 return; | |
250 } | |
251 | |
252 if (ngx_ssl_handshake(c) == NGX_AGAIN) { | |
253 | |
254 s = c->data; | |
255 | |
256 sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); | |
257 | |
258 ngx_add_timer(c->read, sslcf->handshake_timeout); | |
259 | |
260 c->ssl->handler = ngx_stream_ssl_handshake_handler; | |
261 | |
262 return; | |
263 } | |
264 | |
265 ngx_stream_ssl_handshake_handler(c); | |
266 } | |
267 | |
268 | |
269 static void | |
270 ngx_stream_ssl_handshake_handler(ngx_connection_t *c) | |
271 { | |
272 if (!c->ssl->handshaked) { | |
273 ngx_stream_close_connection(c); | |
274 return; | |
275 } | |
276 | |
277 if (c->read->timer_set) { | |
278 ngx_del_timer(c->read); | |
279 } | |
280 | |
281 ngx_stream_init_session(c); | |
282 } | |
283 | |
284 #endif | |
285 | |
286 | |
287 void | |
288 ngx_stream_close_connection(ngx_connection_t *c) | |
289 { | |
290 ngx_pool_t *pool; | |
291 | |
292 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, | |
293 "close stream connection: %d", c->fd); | |
294 | |
295 #if (NGX_STREAM_SSL) | |
296 | |
297 if (c->ssl) { | |
298 if (ngx_ssl_shutdown(c) == NGX_AGAIN) { | |
299 c->ssl->handler = ngx_stream_close_connection; | |
300 return; | |
301 } | |
302 } | |
303 | |
304 #endif | |
305 | |
306 #if (NGX_STAT_STUB) | |
307 (void) ngx_atomic_fetch_add(ngx_stat_active, -1); | |
308 #endif | |
309 | |
310 pool = c->pool; | |
311 | |
312 ngx_close_connection(c); | |
313 | |
314 ngx_destroy_pool(pool); | |
315 } | |
316 | |
317 | |
318 static u_char * | |
319 ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len) | |
320 { | |
321 u_char *p; | |
322 ngx_stream_session_t *s; | |
323 | |
324 if (log->action) { | |
325 p = ngx_snprintf(buf, len, " while %s", log->action); | |
326 len -= p - buf; | |
327 buf = p; | |
328 } | |
329 | |
330 s = log->data; | |
331 | |
6461
a01e315b3a78
Stream: additional logging for UDP.
Vladimir Homutov <vl@nginx.com>
parents:
6436
diff
changeset
|
332 p = ngx_snprintf(buf, len, ", %sclient: %V, server: %V", |
a01e315b3a78
Stream: additional logging for UDP.
Vladimir Homutov <vl@nginx.com>
parents:
6436
diff
changeset
|
333 s->connection->type == SOCK_DGRAM ? "udp " : "", |
6115 | 334 &s->connection->addr_text, |
335 &s->connection->listening->addr_text); | |
6223
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
336 len -= p - buf; |
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
337 buf = p; |
6115 | 338 |
339 if (s->log_handler) { | |
6223
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
340 p = s->log_handler(log, buf, len); |
6115 | 341 } |
342 | |
343 return p; | |
344 } |