Mercurial > hg > nginx-quic
comparison src/http/v3/ngx_http_v3_request.c @ 8692:0d3bf08eaac0 quic
HTTP/3: allowed QUIC stream connection reuse.
A QUIC stream connection is treated as reusable until first bytes of request
arrive, which is also when the request object is now allocated. A connection
closed as a result of draining, is reset with the error code
H3_REQUEST_REJECTED. Such behavior is allowed by quic-http-34:
Once a request stream has been opened, the request MAY be cancelled
by either endpoint. Clients cancel requests if the response is no
longer of interest; servers cancel requests if they are unable to or
choose not to respond.
When the server cancels a request without performing any application
processing, the request is considered "rejected." The server SHOULD
abort its response stream with the error code H3_REQUEST_REJECTED.
The client can treat requests rejected by the server as though they had
never been sent at all, thereby allowing them to be retried later.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Mon, 18 Oct 2021 15:47:06 +0300 |
parents | 925572184d4a |
children | d2c193aa8480 |
comparison
equal
deleted
inserted
replaced
8691:925572184d4a | 8692:0d3bf08eaac0 |
---|---|
8 #include <ngx_config.h> | 8 #include <ngx_config.h> |
9 #include <ngx_core.h> | 9 #include <ngx_core.h> |
10 #include <ngx_http.h> | 10 #include <ngx_http.h> |
11 | 11 |
12 | 12 |
13 static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); | |
13 static void ngx_http_v3_cleanup_request(void *data); | 14 static void ngx_http_v3_cleanup_request(void *data); |
14 static void ngx_http_v3_process_request(ngx_event_t *rev); | 15 static void ngx_http_v3_process_request(ngx_event_t *rev); |
15 static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, | 16 static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, |
16 ngx_str_t *name, ngx_str_t *value); | 17 ngx_str_t *name, ngx_str_t *value); |
17 static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, | 18 static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, |
51 | 52 |
52 | 53 |
53 void | 54 void |
54 ngx_http_v3_init(ngx_connection_t *c) | 55 ngx_http_v3_init(ngx_connection_t *c) |
55 { | 56 { |
56 size_t size; | |
57 uint64_t n; | 57 uint64_t n; |
58 ngx_buf_t *b; | |
59 ngx_event_t *rev; | 58 ngx_event_t *rev; |
60 ngx_pool_cleanup_t *cln; | |
61 ngx_http_request_t *r; | |
62 ngx_http_connection_t *hc; | 59 ngx_http_connection_t *hc; |
63 ngx_http_v3_session_t *h3c; | 60 ngx_http_v3_session_t *h3c; |
64 ngx_http_core_loc_conf_t *clcf; | 61 ngx_http_core_loc_conf_t *clcf; |
65 ngx_http_core_srv_conf_t *cscf; | 62 ngx_http_core_srv_conf_t *cscf; |
66 | 63 |
94 } | 91 } |
95 | 92 |
96 h3c = ngx_http_v3_get_session(c); | 93 h3c = ngx_http_v3_get_session(c); |
97 | 94 |
98 if (h3c->goaway) { | 95 if (h3c->goaway) { |
99 ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); | 96 c->close = 1; |
100 ngx_http_close_connection(c); | 97 ngx_http_close_connection(c); |
101 return; | 98 return; |
102 } | 99 } |
103 | 100 |
104 if (n + 1 == clcf->keepalive_requests | 101 if (n + 1 == clcf->keepalive_requests |
114 | 111 |
115 ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, | 112 ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, |
116 "reached maximum number of requests"); | 113 "reached maximum number of requests"); |
117 } | 114 } |
118 | 115 |
119 cln = ngx_pool_cleanup_add(c->pool, 0); | 116 rev = c->read; |
120 if (cln == NULL) { | 117 rev->handler = ngx_http_v3_wait_request_handler; |
118 c->write->handler = ngx_http_empty_handler; | |
119 | |
120 if (rev->ready) { | |
121 rev->handler(rev); | |
122 return; | |
123 } | |
124 | |
125 cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); | |
126 | |
127 ngx_add_timer(rev, cscf->client_header_timeout); | |
128 ngx_reusable_connection(c, 1); | |
129 | |
130 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
121 ngx_http_close_connection(c); | 131 ngx_http_close_connection(c); |
122 return; | 132 return; |
123 } | 133 } |
124 | 134 } |
125 cln->handler = ngx_http_v3_cleanup_request; | 135 |
126 cln->data = c; | 136 |
127 | 137 static void |
128 h3c->nrequests++; | 138 ngx_http_v3_wait_request_handler(ngx_event_t *rev) |
129 | 139 { |
130 if (h3c->keepalive.timer_set) { | 140 size_t size; |
131 ngx_del_timer(&h3c->keepalive); | 141 ssize_t n; |
132 } | 142 ngx_buf_t *b; |
133 | 143 ngx_connection_t *c; |
144 ngx_pool_cleanup_t *cln; | |
145 ngx_http_request_t *r; | |
146 ngx_http_connection_t *hc; | |
147 ngx_http_v3_session_t *h3c; | |
148 ngx_http_core_srv_conf_t *cscf; | |
149 | |
150 c = rev->data; | |
151 | |
152 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 wait request handler"); | |
153 | |
154 if (rev->timedout) { | |
155 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
156 c->timedout = 1; | |
157 ngx_http_close_connection(c); | |
158 return; | |
159 } | |
160 | |
161 if (c->close) { | |
162 ngx_http_close_connection(c); | |
163 return; | |
164 } | |
165 | |
166 hc = c->data; | |
134 cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); | 167 cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); |
135 | 168 |
136 size = cscf->client_header_buffer_size; | 169 size = cscf->client_header_buffer_size; |
137 | 170 |
138 b = c->buffer; | 171 b = c->buffer; |
157 b->pos = b->start; | 190 b->pos = b->start; |
158 b->last = b->start; | 191 b->last = b->start; |
159 b->end = b->last + size; | 192 b->end = b->last + size; |
160 } | 193 } |
161 | 194 |
195 n = c->recv(c, b->last, size); | |
196 | |
197 if (n == NGX_AGAIN) { | |
198 | |
199 if (!rev->timer_set) { | |
200 ngx_add_timer(rev, cscf->client_header_timeout); | |
201 ngx_reusable_connection(c, 1); | |
202 } | |
203 | |
204 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
205 ngx_http_close_connection(c); | |
206 return; | |
207 } | |
208 | |
209 /* | |
210 * We are trying to not hold c->buffer's memory for an idle connection. | |
211 */ | |
212 | |
213 if (ngx_pfree(c->pool, b->start) == NGX_OK) { | |
214 b->start = NULL; | |
215 } | |
216 | |
217 return; | |
218 } | |
219 | |
220 if (n == NGX_ERROR) { | |
221 ngx_http_close_connection(c); | |
222 return; | |
223 } | |
224 | |
225 if (n == 0) { | |
226 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
227 "client closed connection"); | |
228 ngx_http_close_connection(c); | |
229 return; | |
230 } | |
231 | |
232 b->last += n; | |
233 | |
162 c->log->action = "reading client request"; | 234 c->log->action = "reading client request"; |
235 | |
236 ngx_reusable_connection(c, 0); | |
163 | 237 |
164 r = ngx_http_create_request(c); | 238 r = ngx_http_create_request(c); |
165 if (r == NULL) { | 239 if (r == NULL) { |
166 ngx_http_close_connection(c); | 240 ngx_http_close_connection(c); |
167 return; | 241 return; |
169 | 243 |
170 r->http_version = NGX_HTTP_VERSION_30; | 244 r->http_version = NGX_HTTP_VERSION_30; |
171 | 245 |
172 r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t)); | 246 r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t)); |
173 if (r->v3_parse == NULL) { | 247 if (r->v3_parse == NULL) { |
174 ngx_http_close_connection(c); | 248 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); |
175 return; | 249 return; |
176 } | 250 } |
177 | 251 |
178 r->v3_parse->header_limit = cscf->large_client_header_buffers.size | 252 r->v3_parse->header_limit = cscf->large_client_header_buffers.size |
179 * cscf->large_client_header_buffers.num; | 253 * cscf->large_client_header_buffers.num; |
180 | 254 |
181 c->data = r; | 255 c->data = r; |
182 c->requests = n + 1; | 256 c->requests = (c->quic->id >> 2) + 1; |
183 | 257 |
184 rev = c->read; | 258 cln = ngx_pool_cleanup_add(r->pool, 0); |
259 if (cln == NULL) { | |
260 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
261 return; | |
262 } | |
263 | |
264 cln->handler = ngx_http_v3_cleanup_request; | |
265 cln->data = r; | |
266 | |
267 h3c = ngx_http_v3_get_session(c); | |
268 h3c->nrequests++; | |
269 | |
270 if (h3c->keepalive.timer_set) { | |
271 ngx_del_timer(&h3c->keepalive); | |
272 } | |
273 | |
185 rev->handler = ngx_http_v3_process_request; | 274 rev->handler = ngx_http_v3_process_request; |
186 | |
187 ngx_http_v3_process_request(rev); | 275 ngx_http_v3_process_request(rev); |
276 } | |
277 | |
278 | |
279 void | |
280 ngx_http_v3_reset_connection(ngx_connection_t *c) | |
281 { | |
282 if (c->timedout) { | |
283 ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); | |
284 | |
285 } else if (c->close) { | |
286 ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); | |
287 | |
288 } else if (c->requests == 0 || c->error) { | |
289 ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR); | |
290 } | |
188 } | 291 } |
189 | 292 |
190 | 293 |
191 static void | 294 static void |
192 ngx_http_v3_cleanup_request(void *data) | 295 ngx_http_v3_cleanup_request(void *data) |
193 { | 296 { |
194 ngx_connection_t *c = data; | 297 ngx_http_request_t *r = data; |
195 | 298 |
299 ngx_connection_t *c; | |
196 ngx_http_v3_session_t *h3c; | 300 ngx_http_v3_session_t *h3c; |
197 ngx_http_core_loc_conf_t *clcf; | 301 ngx_http_core_loc_conf_t *clcf; |
302 | |
303 c = r->connection; | |
304 | |
305 if (!r->response_sent) { | |
306 c->error = 1; | |
307 } | |
198 | 308 |
199 h3c = ngx_http_v3_get_session(c); | 309 h3c = ngx_http_v3_get_session(c); |
200 | 310 |
201 if (--h3c->nrequests == 0) { | 311 if (--h3c->nrequests == 0) { |
202 clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); | 312 clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); |