comparison src/http/v3/ngx_http_v3_request.c @ 8903: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
8902:925572184d4a 8903: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);