Mercurial > hg > nginx-quic
comparison src/http/v3/ngx_http_v3_filter_module.c @ 8237:1efee5e4194c quic
HTTP/3: introduced ngx_http_v3_filter.
The filter is responsible for creating HTTP/3 response header and body.
The change removes differences to the default branch for
ngx_http_chunked_filter_module and ngx_http_header_filter_module.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Fri, 27 Nov 2020 17:46:21 +0000 |
parents | |
children | 6bb924b00e30 |
comparison
equal
deleted
inserted
replaced
8236:e9bd4305e68b | 8237:1efee5e4194c |
---|---|
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_http.h> | |
11 | |
12 | |
13 /* static table indices */ | |
14 #define NGX_HTTP_V3_HEADER_AUTHORITY 0 | |
15 #define NGX_HTTP_V3_HEADER_PATH_ROOT 1 | |
16 #define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 | |
17 #define NGX_HTTP_V3_HEADER_DATE 6 | |
18 #define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 | |
19 #define NGX_HTTP_V3_HEADER_LOCATION 12 | |
20 #define NGX_HTTP_V3_HEADER_METHOD_GET 17 | |
21 #define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 | |
22 #define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 | |
23 #define NGX_HTTP_V3_HEADER_STATUS_200 25 | |
24 #define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 | |
25 #define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 | |
26 #define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 | |
27 #define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 | |
28 #define NGX_HTTP_V3_HEADER_SERVER 92 | |
29 #define NGX_HTTP_V3_HEADER_USER_AGENT 95 | |
30 | |
31 | |
32 typedef struct { | |
33 ngx_chain_t *free; | |
34 ngx_chain_t *busy; | |
35 } ngx_http_v3_filter_ctx_t; | |
36 | |
37 | |
38 static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r); | |
39 static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, | |
40 ngx_chain_t ***out); | |
41 static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, | |
42 ngx_str_t *path, ngx_chain_t ***out); | |
43 static ngx_int_t ngx_http_v3_create_push_request( | |
44 ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); | |
45 static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, | |
46 const char *name, ngx_str_t *value); | |
47 static void ngx_http_v3_push_request_handler(ngx_event_t *ev); | |
48 static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, | |
49 ngx_str_t *path, uint64_t push_id); | |
50 static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, | |
51 ngx_chain_t *in); | |
52 static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); | |
53 static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf); | |
54 | |
55 | |
56 static ngx_http_module_t ngx_http_v3_filter_module_ctx = { | |
57 NULL, /* preconfiguration */ | |
58 ngx_http_v3_filter_init, /* postconfiguration */ | |
59 | |
60 NULL, /* create main configuration */ | |
61 NULL, /* init main configuration */ | |
62 | |
63 NULL, /* create server configuration */ | |
64 NULL, /* merge server configuration */ | |
65 | |
66 NULL, /* create location configuration */ | |
67 NULL /* merge location configuration */ | |
68 }; | |
69 | |
70 | |
71 ngx_module_t ngx_http_v3_filter_module = { | |
72 NGX_MODULE_V1, | |
73 &ngx_http_v3_filter_module_ctx, /* module context */ | |
74 NULL, /* module directives */ | |
75 NGX_HTTP_MODULE, /* module type */ | |
76 NULL, /* init master */ | |
77 NULL, /* init module */ | |
78 NULL, /* init process */ | |
79 NULL, /* init thread */ | |
80 NULL, /* exit thread */ | |
81 NULL, /* exit process */ | |
82 NULL, /* exit master */ | |
83 NGX_MODULE_V1_PADDING | |
84 }; | |
85 | |
86 | |
87 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
88 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | |
89 | |
90 | |
91 static ngx_int_t | |
92 ngx_http_v3_header_filter(ngx_http_request_t *r) | |
93 { | |
94 u_char *p; | |
95 size_t len, n; | |
96 ngx_buf_t *b; | |
97 ngx_str_t host; | |
98 ngx_uint_t i, port; | |
99 ngx_chain_t *out, *hl, *cl, **ll; | |
100 ngx_list_part_t *part; | |
101 ngx_table_elt_t *header; | |
102 ngx_connection_t *c; | |
103 ngx_http_v3_filter_ctx_t *ctx; | |
104 ngx_http_core_loc_conf_t *clcf; | |
105 ngx_http_core_srv_conf_t *cscf; | |
106 u_char addr[NGX_SOCKADDR_STRLEN]; | |
107 | |
108 if (r->http_version != NGX_HTTP_VERSION_30) { | |
109 return ngx_http_next_header_filter(r); | |
110 } | |
111 | |
112 if (r->header_sent) { | |
113 return NGX_OK; | |
114 } | |
115 | |
116 r->header_sent = 1; | |
117 | |
118 if (r != r->main) { | |
119 return NGX_OK; | |
120 } | |
121 | |
122 if (r->method == NGX_HTTP_HEAD) { | |
123 r->header_only = 1; | |
124 } | |
125 | |
126 if (r->headers_out.last_modified_time != -1) { | |
127 if (r->headers_out.status != NGX_HTTP_OK | |
128 && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT | |
129 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED) | |
130 { | |
131 r->headers_out.last_modified_time = -1; | |
132 r->headers_out.last_modified = NULL; | |
133 } | |
134 } | |
135 | |
136 if (r->headers_out.status == NGX_HTTP_NO_CONTENT) { | |
137 r->header_only = 1; | |
138 ngx_str_null(&r->headers_out.content_type); | |
139 r->headers_out.last_modified_time = -1; | |
140 r->headers_out.last_modified = NULL; | |
141 r->headers_out.content_length = NULL; | |
142 r->headers_out.content_length_n = -1; | |
143 } | |
144 | |
145 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { | |
146 r->header_only = 1; | |
147 } | |
148 | |
149 c = r->connection; | |
150 | |
151 out = NULL; | |
152 ll = &out; | |
153 | |
154 if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 | |
155 && r->method != NGX_HTTP_HEAD) | |
156 { | |
157 if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { | |
158 return NGX_ERROR; | |
159 } | |
160 } | |
161 | |
162 len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); | |
163 | |
164 if (r->headers_out.status == NGX_HTTP_OK) { | |
165 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
166 NGX_HTTP_V3_HEADER_STATUS_200); | |
167 | |
168 } else { | |
169 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
170 NGX_HTTP_V3_HEADER_STATUS_200, | |
171 NULL, 3); | |
172 } | |
173 | |
174 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
175 | |
176 if (r->headers_out.server == NULL) { | |
177 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
178 n = sizeof(NGINX_VER) - 1; | |
179 | |
180 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
181 n = sizeof(NGINX_VER_BUILD) - 1; | |
182 | |
183 } else { | |
184 n = sizeof("nginx") - 1; | |
185 } | |
186 | |
187 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
188 NGX_HTTP_V3_HEADER_SERVER, | |
189 NULL, n); | |
190 } | |
191 | |
192 if (r->headers_out.date == NULL) { | |
193 len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, | |
194 NULL, ngx_cached_http_time.len); | |
195 } | |
196 | |
197 if (r->headers_out.content_type.len) { | |
198 n = r->headers_out.content_type.len; | |
199 | |
200 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
201 && r->headers_out.charset.len) | |
202 { | |
203 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
204 } | |
205 | |
206 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
207 NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, | |
208 NULL, n); | |
209 } | |
210 | |
211 if (r->headers_out.content_length == NULL) { | |
212 if (r->headers_out.content_length_n > 0) { | |
213 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
214 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, | |
215 NULL, NGX_OFF_T_LEN); | |
216 | |
217 } else if (r->headers_out.content_length_n == 0) { | |
218 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
219 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); | |
220 } | |
221 } | |
222 | |
223 if (r->headers_out.last_modified == NULL | |
224 && r->headers_out.last_modified_time != -1) | |
225 { | |
226 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
227 NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, | |
228 sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); | |
229 } | |
230 | |
231 if (r->headers_out.location | |
232 && r->headers_out.location->value.len | |
233 && r->headers_out.location->value.data[0] == '/' | |
234 && clcf->absolute_redirect) | |
235 { | |
236 r->headers_out.location->hash = 0; | |
237 | |
238 if (clcf->server_name_in_redirect) { | |
239 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
240 host = cscf->server_name; | |
241 | |
242 } else if (r->headers_in.server.len) { | |
243 host = r->headers_in.server; | |
244 | |
245 } else { | |
246 host.len = NGX_SOCKADDR_STRLEN; | |
247 host.data = addr; | |
248 | |
249 if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { | |
250 return NGX_ERROR; | |
251 } | |
252 } | |
253 | |
254 port = ngx_inet_get_port(c->local_sockaddr); | |
255 | |
256 n = sizeof("https://") - 1 + host.len | |
257 + r->headers_out.location->value.len; | |
258 | |
259 if (clcf->port_in_redirect) { | |
260 port = (port == 443) ? 0 : port; | |
261 | |
262 } else { | |
263 port = 0; | |
264 } | |
265 | |
266 if (port) { | |
267 n += sizeof(":65535") - 1; | |
268 } | |
269 | |
270 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
271 NGX_HTTP_V3_HEADER_LOCATION, NULL, n); | |
272 | |
273 } else { | |
274 ngx_str_null(&host); | |
275 port = 0; | |
276 } | |
277 | |
278 #if (NGX_HTTP_GZIP) | |
279 if (r->gzip_vary) { | |
280 if (clcf->gzip_vary) { | |
281 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
282 NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); | |
283 | |
284 } else { | |
285 r->gzip_vary = 0; | |
286 } | |
287 } | |
288 #endif | |
289 | |
290 part = &r->headers_out.headers.part; | |
291 header = part->elts; | |
292 | |
293 for (i = 0; /* void */; i++) { | |
294 | |
295 if (i >= part->nelts) { | |
296 if (part->next == NULL) { | |
297 break; | |
298 } | |
299 | |
300 part = part->next; | |
301 header = part->elts; | |
302 i = 0; | |
303 } | |
304 | |
305 if (header[i].hash == 0) { | |
306 continue; | |
307 } | |
308 | |
309 len += ngx_http_v3_encode_header_l(NULL, &header[i].key, | |
310 &header[i].value); | |
311 } | |
312 | |
313 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); | |
314 | |
315 b = ngx_create_temp_buf(r->pool, len); | |
316 if (b == NULL) { | |
317 return NGX_ERROR; | |
318 } | |
319 | |
320 b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, | |
321 0, 0, 0); | |
322 | |
323 if (r->headers_out.status == NGX_HTTP_OK) { | |
324 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
325 NGX_HTTP_V3_HEADER_STATUS_200); | |
326 | |
327 } else { | |
328 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
329 NGX_HTTP_V3_HEADER_STATUS_200, | |
330 NULL, 3); | |
331 b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); | |
332 } | |
333 | |
334 if (r->headers_out.server == NULL) { | |
335 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
336 p = (u_char *) NGINX_VER; | |
337 n = sizeof(NGINX_VER) - 1; | |
338 | |
339 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
340 p = (u_char *) NGINX_VER_BUILD; | |
341 n = sizeof(NGINX_VER_BUILD) - 1; | |
342 | |
343 } else { | |
344 p = (u_char *) "nginx"; | |
345 n = sizeof("nginx") - 1; | |
346 } | |
347 | |
348 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
349 NGX_HTTP_V3_HEADER_SERVER, | |
350 p, n); | |
351 } | |
352 | |
353 if (r->headers_out.date == NULL) { | |
354 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
355 NGX_HTTP_V3_HEADER_DATE, | |
356 ngx_cached_http_time.data, | |
357 ngx_cached_http_time.len); | |
358 } | |
359 | |
360 if (r->headers_out.content_type.len) { | |
361 n = r->headers_out.content_type.len; | |
362 | |
363 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
364 && r->headers_out.charset.len) | |
365 { | |
366 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
367 } | |
368 | |
369 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
370 NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, | |
371 NULL, n); | |
372 | |
373 p = b->last; | |
374 b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, | |
375 r->headers_out.content_type.len); | |
376 | |
377 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
378 && r->headers_out.charset.len) | |
379 { | |
380 b->last = ngx_cpymem(b->last, "; charset=", | |
381 sizeof("; charset=") - 1); | |
382 b->last = ngx_cpymem(b->last, r->headers_out.charset.data, | |
383 r->headers_out.charset.len); | |
384 | |
385 /* update r->headers_out.content_type for possible logging */ | |
386 | |
387 r->headers_out.content_type.len = b->last - p; | |
388 r->headers_out.content_type.data = p; | |
389 } | |
390 } | |
391 | |
392 if (r->headers_out.content_length == NULL) { | |
393 if (r->headers_out.content_length_n > 0) { | |
394 p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); | |
395 n = p - b->last; | |
396 | |
397 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
398 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, | |
399 NULL, n); | |
400 | |
401 b->last = ngx_sprintf(b->last, "%O", | |
402 r->headers_out.content_length_n); | |
403 | |
404 } else if (r->headers_out.content_length_n == 0) { | |
405 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
406 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); | |
407 } | |
408 } | |
409 | |
410 if (r->headers_out.last_modified == NULL | |
411 && r->headers_out.last_modified_time != -1) | |
412 { | |
413 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
414 NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, | |
415 sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); | |
416 | |
417 b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); | |
418 } | |
419 | |
420 if (host.data) { | |
421 n = sizeof("https://") - 1 + host.len | |
422 + r->headers_out.location->value.len; | |
423 | |
424 if (port) { | |
425 n += ngx_sprintf(b->last, ":%ui", port) - b->last; | |
426 } | |
427 | |
428 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
429 NGX_HTTP_V3_HEADER_LOCATION, | |
430 NULL, n); | |
431 | |
432 p = b->last; | |
433 b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); | |
434 b->last = ngx_cpymem(b->last, host.data, host.len); | |
435 | |
436 if (port) { | |
437 b->last = ngx_sprintf(b->last, ":%ui", port); | |
438 } | |
439 | |
440 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, | |
441 r->headers_out.location->value.len); | |
442 | |
443 /* update r->headers_out.location->value for possible logging */ | |
444 | |
445 r->headers_out.location->value.len = b->last - p; | |
446 r->headers_out.location->value.data = p; | |
447 ngx_str_set(&r->headers_out.location->key, "Location"); | |
448 } | |
449 | |
450 #if (NGX_HTTP_GZIP) | |
451 if (r->gzip_vary) { | |
452 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
453 NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); | |
454 } | |
455 #endif | |
456 | |
457 part = &r->headers_out.headers.part; | |
458 header = part->elts; | |
459 | |
460 for (i = 0; /* void */; i++) { | |
461 | |
462 if (i >= part->nelts) { | |
463 if (part->next == NULL) { | |
464 break; | |
465 } | |
466 | |
467 part = part->next; | |
468 header = part->elts; | |
469 i = 0; | |
470 } | |
471 | |
472 if (header[i].hash == 0) { | |
473 continue; | |
474 } | |
475 | |
476 b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, | |
477 &header[i].key, | |
478 &header[i].value); | |
479 } | |
480 | |
481 if (r->header_only) { | |
482 b->last_buf = 1; | |
483 } | |
484 | |
485 cl = ngx_alloc_chain_link(c->pool); | |
486 if (cl == NULL) { | |
487 return NGX_ERROR; | |
488 } | |
489 | |
490 cl->buf = b; | |
491 cl->next = NULL; | |
492 | |
493 n = b->last - b->pos; | |
494 | |
495 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) | |
496 + ngx_http_v3_encode_varlen_int(NULL, n); | |
497 | |
498 b = ngx_create_temp_buf(c->pool, len); | |
499 if (b == NULL) { | |
500 return NGX_ERROR; | |
501 } | |
502 | |
503 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
504 NGX_HTTP_V3_FRAME_HEADERS); | |
505 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); | |
506 | |
507 hl = ngx_alloc_chain_link(c->pool); | |
508 if (hl == NULL) { | |
509 return NGX_ERROR; | |
510 } | |
511 | |
512 hl->buf = b; | |
513 hl->next = cl; | |
514 | |
515 *ll = hl; | |
516 ll = &cl->next; | |
517 | |
518 if (r->headers_out.content_length_n >= 0 && !r->header_only) { | |
519 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) | |
520 + ngx_http_v3_encode_varlen_int(NULL, | |
521 r->headers_out.content_length_n); | |
522 | |
523 b = ngx_create_temp_buf(c->pool, len); | |
524 if (b == NULL) { | |
525 return NGX_ERROR; | |
526 } | |
527 | |
528 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
529 NGX_HTTP_V3_FRAME_DATA); | |
530 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
531 r->headers_out.content_length_n); | |
532 | |
533 cl = ngx_alloc_chain_link(c->pool); | |
534 if (cl == NULL) { | |
535 return NGX_ERROR; | |
536 } | |
537 | |
538 cl->buf = b; | |
539 cl->next = NULL; | |
540 | |
541 *ll = cl; | |
542 | |
543 } else { | |
544 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_filter_ctx_t)); | |
545 if (ctx == NULL) { | |
546 return NGX_ERROR; | |
547 } | |
548 | |
549 ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module); | |
550 } | |
551 | |
552 return ngx_http_write_filter(r, out); | |
553 } | |
554 | |
555 | |
556 static ngx_int_t | |
557 ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) | |
558 { | |
559 u_char *start, *end, *last; | |
560 ngx_str_t path; | |
561 ngx_int_t rc; | |
562 ngx_uint_t i, push; | |
563 ngx_table_elt_t **h; | |
564 ngx_http_v3_loc_conf_t *h3lcf; | |
565 ngx_http_complex_value_t *pushes; | |
566 | |
567 h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); | |
568 | |
569 if (h3lcf->pushes) { | |
570 pushes = h3lcf->pushes->elts; | |
571 | |
572 for (i = 0; i < h3lcf->pushes->nelts; i++) { | |
573 | |
574 if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { | |
575 return NGX_ERROR; | |
576 } | |
577 | |
578 if (path.len == 0) { | |
579 continue; | |
580 } | |
581 | |
582 if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { | |
583 continue; | |
584 } | |
585 | |
586 rc = ngx_http_v3_push_resource(r, &path, out); | |
587 | |
588 if (rc == NGX_ERROR) { | |
589 return NGX_ERROR; | |
590 } | |
591 | |
592 if (rc == NGX_ABORT) { | |
593 return NGX_OK; | |
594 } | |
595 | |
596 /* NGX_OK, NGX_DECLINED */ | |
597 } | |
598 } | |
599 | |
600 if (!h3lcf->push_preload) { | |
601 return NGX_OK; | |
602 } | |
603 | |
604 h = r->headers_out.link.elts; | |
605 | |
606 for (i = 0; i < r->headers_out.link.nelts; i++) { | |
607 | |
608 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
609 "http3 parse link: \"%V\"", &h[i]->value); | |
610 | |
611 start = h[i]->value.data; | |
612 end = h[i]->value.data + h[i]->value.len; | |
613 | |
614 next_link: | |
615 | |
616 while (start < end && *start == ' ') { start++; } | |
617 | |
618 if (start == end || *start++ != '<') { | |
619 continue; | |
620 } | |
621 | |
622 while (start < end && *start == ' ') { start++; } | |
623 | |
624 for (last = start; last < end && *last != '>'; last++) { | |
625 /* void */ | |
626 } | |
627 | |
628 if (last == start || last == end) { | |
629 continue; | |
630 } | |
631 | |
632 path.len = last - start; | |
633 path.data = start; | |
634 | |
635 start = last + 1; | |
636 | |
637 while (start < end && *start == ' ') { start++; } | |
638 | |
639 if (start == end) { | |
640 continue; | |
641 } | |
642 | |
643 if (*start == ',') { | |
644 start++; | |
645 goto next_link; | |
646 } | |
647 | |
648 if (*start++ != ';') { | |
649 continue; | |
650 } | |
651 | |
652 last = ngx_strlchr(start, end, ','); | |
653 | |
654 if (last == NULL) { | |
655 last = end; | |
656 } | |
657 | |
658 push = 0; | |
659 | |
660 for ( ;; ) { | |
661 | |
662 while (start < last && *start == ' ') { start++; } | |
663 | |
664 if (last - start >= 6 | |
665 && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) | |
666 { | |
667 start += 6; | |
668 | |
669 if (start == last || *start == ' ' || *start == ';') { | |
670 push = 0; | |
671 break; | |
672 } | |
673 | |
674 goto next_param; | |
675 } | |
676 | |
677 if (last - start >= 11 | |
678 && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) | |
679 { | |
680 start += 11; | |
681 | |
682 if (start == last || *start == ' ' || *start == ';') { | |
683 push = 1; | |
684 } | |
685 | |
686 goto next_param; | |
687 } | |
688 | |
689 if (last - start >= 4 | |
690 && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) | |
691 { | |
692 start += 4; | |
693 | |
694 while (start < last && *start == ' ') { start++; } | |
695 | |
696 if (start == last || *start++ != '"') { | |
697 goto next_param; | |
698 } | |
699 | |
700 for ( ;; ) { | |
701 | |
702 while (start < last && *start == ' ') { start++; } | |
703 | |
704 if (last - start >= 7 | |
705 && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) | |
706 { | |
707 start += 7; | |
708 | |
709 if (start < last && (*start == ' ' || *start == '"')) { | |
710 push = 1; | |
711 break; | |
712 } | |
713 } | |
714 | |
715 while (start < last && *start != ' ' && *start != '"') { | |
716 start++; | |
717 } | |
718 | |
719 if (start == last) { | |
720 break; | |
721 } | |
722 | |
723 if (*start == '"') { | |
724 break; | |
725 } | |
726 | |
727 start++; | |
728 } | |
729 } | |
730 | |
731 next_param: | |
732 | |
733 start = ngx_strlchr(start, last, ';'); | |
734 | |
735 if (start == NULL) { | |
736 break; | |
737 } | |
738 | |
739 start++; | |
740 } | |
741 | |
742 if (push) { | |
743 while (path.len && path.data[path.len - 1] == ' ') { | |
744 path.len--; | |
745 } | |
746 } | |
747 | |
748 if (push && path.len | |
749 && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) | |
750 { | |
751 rc = ngx_http_v3_push_resource(r, &path, out); | |
752 | |
753 if (rc == NGX_ERROR) { | |
754 return NGX_ERROR; | |
755 } | |
756 | |
757 if (rc == NGX_ABORT) { | |
758 return NGX_OK; | |
759 } | |
760 | |
761 /* NGX_OK, NGX_DECLINED */ | |
762 } | |
763 | |
764 if (last < end) { | |
765 start = last + 1; | |
766 goto next_link; | |
767 } | |
768 } | |
769 | |
770 return NGX_OK; | |
771 } | |
772 | |
773 | |
774 static ngx_int_t | |
775 ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, | |
776 ngx_chain_t ***ll) | |
777 { | |
778 uint64_t push_id; | |
779 ngx_int_t rc; | |
780 ngx_chain_t *cl; | |
781 ngx_connection_t *c; | |
782 ngx_http_v3_srv_conf_t *h3scf; | |
783 ngx_http_v3_connection_t *h3c; | |
784 | |
785 c = r->connection; | |
786 h3c = c->quic->parent->data; | |
787 h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); | |
788 | |
789 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
790 "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", | |
791 path, h3c->npushing, h3scf->max_concurrent_pushes, | |
792 h3c->next_push_id, h3c->max_push_id); | |
793 | |
794 if (!ngx_path_separator(path->data[0])) { | |
795 ngx_log_error(NGX_LOG_WARN, c->log, 0, | |
796 "non-absolute path \"%V\" not pushed", path); | |
797 return NGX_DECLINED; | |
798 } | |
799 | |
800 if (h3c->next_push_id > h3c->max_push_id) { | |
801 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
802 "http3 abort pushes due to max_push_id"); | |
803 return NGX_ABORT; | |
804 } | |
805 | |
806 if (h3c->npushing >= h3scf->max_concurrent_pushes) { | |
807 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
808 "http3 abort pushes due to max_concurrent_pushes"); | |
809 return NGX_ABORT; | |
810 } | |
811 | |
812 push_id = h3c->next_push_id++; | |
813 | |
814 rc = ngx_http_v3_create_push_request(r, path, push_id); | |
815 if (rc != NGX_OK) { | |
816 return rc; | |
817 } | |
818 | |
819 cl = ngx_http_v3_create_push_promise(r, path, push_id); | |
820 if (cl == NULL) { | |
821 return NGX_ERROR; | |
822 } | |
823 | |
824 for (**ll = cl; **ll; *ll = &(**ll)->next); | |
825 | |
826 return NGX_OK; | |
827 } | |
828 | |
829 | |
830 static ngx_int_t | |
831 ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, | |
832 uint64_t push_id) | |
833 { | |
834 ngx_pool_t *pool; | |
835 ngx_connection_t *c, *pc; | |
836 ngx_http_request_t *r; | |
837 ngx_http_log_ctx_t *ctx; | |
838 ngx_http_connection_t *hc; | |
839 ngx_http_core_srv_conf_t *cscf; | |
840 ngx_http_v3_connection_t *h3c; | |
841 | |
842 pc = pr->connection; | |
843 | |
844 r = NULL; | |
845 | |
846 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
847 "http3 create push request id:%uL", push_id); | |
848 | |
849 c = ngx_http_v3_create_push_stream(pc, push_id); | |
850 if (c == NULL) { | |
851 return NGX_ABORT; | |
852 } | |
853 | |
854 hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); | |
855 if (hc == NULL) { | |
856 goto failed; | |
857 } | |
858 | |
859 h3c = c->quic->parent->data; | |
860 ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); | |
861 c->data = hc; | |
862 | |
863 ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); | |
864 if (ctx == NULL) { | |
865 goto failed; | |
866 } | |
867 | |
868 ctx->connection = c; | |
869 ctx->request = NULL; | |
870 ctx->current_request = NULL; | |
871 | |
872 c->log->handler = ngx_http_log_error; | |
873 c->log->data = ctx; | |
874 c->log->action = "processing pushed request headers"; | |
875 | |
876 c->log_error = NGX_ERROR_INFO; | |
877 | |
878 r = ngx_http_create_request(c); | |
879 if (r == NULL) { | |
880 goto failed; | |
881 } | |
882 | |
883 c->data = r; | |
884 | |
885 ngx_str_set(&r->http_protocol, "HTTP/3.0"); | |
886 | |
887 r->method_name = ngx_http_core_get_method; | |
888 r->method = NGX_HTTP_GET; | |
889 | |
890 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
891 | |
892 r->header_in = ngx_create_temp_buf(r->pool, | |
893 cscf->client_header_buffer_size); | |
894 if (r->header_in == NULL) { | |
895 goto failed; | |
896 } | |
897 | |
898 if (ngx_list_init(&r->headers_in.headers, r->pool, 4, | |
899 sizeof(ngx_table_elt_t)) | |
900 != NGX_OK) | |
901 { | |
902 goto failed; | |
903 } | |
904 | |
905 r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; | |
906 | |
907 r->schema.data = ngx_pstrdup(r->pool, &pr->schema); | |
908 if (r->schema.data == NULL) { | |
909 goto failed; | |
910 } | |
911 | |
912 r->schema.len = pr->schema.len; | |
913 | |
914 r->uri_start = ngx_pstrdup(r->pool, path); | |
915 if (r->uri_start == NULL) { | |
916 goto failed; | |
917 } | |
918 | |
919 r->uri_end = r->uri_start + path->len; | |
920 | |
921 if (ngx_http_parse_uri(r) != NGX_OK) { | |
922 goto failed; | |
923 } | |
924 | |
925 if (ngx_http_process_request_uri(r) != NGX_OK) { | |
926 goto failed; | |
927 } | |
928 | |
929 if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) | |
930 != NGX_OK) | |
931 { | |
932 goto failed; | |
933 } | |
934 | |
935 if (pr->headers_in.accept_encoding) { | |
936 if (ngx_http_v3_set_push_header(r, "accept-encoding", | |
937 &pr->headers_in.accept_encoding->value) | |
938 != NGX_OK) | |
939 { | |
940 goto failed; | |
941 } | |
942 } | |
943 | |
944 if (pr->headers_in.accept_language) { | |
945 if (ngx_http_v3_set_push_header(r, "accept-language", | |
946 &pr->headers_in.accept_language->value) | |
947 != NGX_OK) | |
948 { | |
949 goto failed; | |
950 } | |
951 } | |
952 | |
953 if (pr->headers_in.user_agent) { | |
954 if (ngx_http_v3_set_push_header(r, "user-agent", | |
955 &pr->headers_in.user_agent->value) | |
956 != NGX_OK) | |
957 { | |
958 goto failed; | |
959 } | |
960 } | |
961 | |
962 c->read->handler = ngx_http_v3_push_request_handler; | |
963 c->read->handler = ngx_http_v3_push_request_handler; | |
964 | |
965 ngx_post_event(c->read, &ngx_posted_events); | |
966 | |
967 return NGX_OK; | |
968 | |
969 failed: | |
970 | |
971 if (r) { | |
972 ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
973 } | |
974 | |
975 c->destroyed = 1; | |
976 | |
977 pool = c->pool; | |
978 | |
979 ngx_close_connection(c); | |
980 | |
981 ngx_destroy_pool(pool); | |
982 | |
983 return NGX_ERROR; | |
984 } | |
985 | |
986 | |
987 static ngx_int_t | |
988 ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, | |
989 ngx_str_t *value) | |
990 { | |
991 u_char *p; | |
992 ngx_table_elt_t *h; | |
993 ngx_http_header_t *hh; | |
994 ngx_http_core_main_conf_t *cmcf; | |
995 | |
996 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
997 "http3 push header \"%s\": \"%V\"", name, value); | |
998 | |
999 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); | |
1000 | |
1001 p = ngx_pnalloc(r->pool, value->len + 1); | |
1002 if (p == NULL) { | |
1003 return NGX_ERROR; | |
1004 } | |
1005 | |
1006 ngx_memcpy(p, value->data, value->len); | |
1007 p[value->len] = '\0'; | |
1008 | |
1009 h = ngx_list_push(&r->headers_in.headers); | |
1010 if (h == NULL) { | |
1011 return NGX_ERROR; | |
1012 } | |
1013 | |
1014 h->key.data = (u_char *) name; | |
1015 h->key.len = ngx_strlen(name); | |
1016 h->hash = ngx_hash_key(h->key.data, h->key.len); | |
1017 h->lowcase_key = (u_char *) name; | |
1018 h->value.data = p; | |
1019 h->value.len = value->len; | |
1020 | |
1021 hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, | |
1022 h->lowcase_key, h->key.len); | |
1023 | |
1024 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { | |
1025 return NGX_ERROR; | |
1026 } | |
1027 | |
1028 return NGX_OK; | |
1029 } | |
1030 | |
1031 | |
1032 static void | |
1033 ngx_http_v3_push_request_handler(ngx_event_t *ev) | |
1034 { | |
1035 ngx_connection_t *c; | |
1036 ngx_http_request_t *r; | |
1037 | |
1038 c = ev->data; | |
1039 r = c->data; | |
1040 | |
1041 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); | |
1042 | |
1043 ngx_http_process_request(r); | |
1044 } | |
1045 | |
1046 | |
1047 static ngx_chain_t * | |
1048 ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, | |
1049 uint64_t push_id) | |
1050 { | |
1051 size_t n, len; | |
1052 ngx_buf_t *b; | |
1053 ngx_chain_t *hl, *cl; | |
1054 | |
1055 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1056 "http3 create push promise id:%uL", push_id); | |
1057 | |
1058 len = ngx_http_v3_encode_varlen_int(NULL, push_id); | |
1059 | |
1060 len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); | |
1061 | |
1062 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1063 NGX_HTTP_V3_HEADER_METHOD_GET); | |
1064 | |
1065 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1066 NGX_HTTP_V3_HEADER_AUTHORITY, | |
1067 NULL, r->headers_in.server.len); | |
1068 | |
1069 if (path->len == 1 && path->data[0] == '/') { | |
1070 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1071 NGX_HTTP_V3_HEADER_PATH_ROOT); | |
1072 | |
1073 } else { | |
1074 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1075 NGX_HTTP_V3_HEADER_PATH_ROOT, | |
1076 NULL, path->len); | |
1077 } | |
1078 | |
1079 if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { | |
1080 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1081 NGX_HTTP_V3_HEADER_SCHEME_HTTPS); | |
1082 | |
1083 } else if (r->schema.len == 4 | |
1084 && ngx_strncmp(r->schema.data, "http", 4) == 0) | |
1085 { | |
1086 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1087 NGX_HTTP_V3_HEADER_SCHEME_HTTP); | |
1088 | |
1089 } else { | |
1090 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1091 NGX_HTTP_V3_HEADER_SCHEME_HTTP, | |
1092 NULL, r->schema.len); | |
1093 } | |
1094 | |
1095 if (r->headers_in.accept_encoding) { | |
1096 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1097 NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, | |
1098 r->headers_in.accept_encoding->value.len); | |
1099 } | |
1100 | |
1101 if (r->headers_in.accept_language) { | |
1102 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1103 NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, | |
1104 r->headers_in.accept_language->value.len); | |
1105 } | |
1106 | |
1107 if (r->headers_in.user_agent) { | |
1108 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1109 NGX_HTTP_V3_HEADER_USER_AGENT, NULL, | |
1110 r->headers_in.user_agent->value.len); | |
1111 } | |
1112 | |
1113 b = ngx_create_temp_buf(r->pool, len); | |
1114 if (b == NULL) { | |
1115 return NULL; | |
1116 } | |
1117 | |
1118 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); | |
1119 | |
1120 b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, | |
1121 0, 0, 0); | |
1122 | |
1123 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1124 NGX_HTTP_V3_HEADER_METHOD_GET); | |
1125 | |
1126 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1127 NGX_HTTP_V3_HEADER_AUTHORITY, | |
1128 r->headers_in.server.data, | |
1129 r->headers_in.server.len); | |
1130 | |
1131 if (path->len == 1 && path->data[0] == '/') { | |
1132 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1133 NGX_HTTP_V3_HEADER_PATH_ROOT); | |
1134 | |
1135 } else { | |
1136 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1137 NGX_HTTP_V3_HEADER_PATH_ROOT, | |
1138 path->data, path->len); | |
1139 } | |
1140 | |
1141 if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { | |
1142 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1143 NGX_HTTP_V3_HEADER_SCHEME_HTTPS); | |
1144 | |
1145 } else if (r->schema.len == 4 | |
1146 && ngx_strncmp(r->schema.data, "http", 4) == 0) | |
1147 { | |
1148 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1149 NGX_HTTP_V3_HEADER_SCHEME_HTTP); | |
1150 | |
1151 } else { | |
1152 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1153 NGX_HTTP_V3_HEADER_SCHEME_HTTP, | |
1154 r->schema.data, r->schema.len); | |
1155 } | |
1156 | |
1157 if (r->headers_in.accept_encoding) { | |
1158 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1159 NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, | |
1160 r->headers_in.accept_encoding->value.data, | |
1161 r->headers_in.accept_encoding->value.len); | |
1162 } | |
1163 | |
1164 if (r->headers_in.accept_language) { | |
1165 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1166 NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, | |
1167 r->headers_in.accept_language->value.data, | |
1168 r->headers_in.accept_language->value.len); | |
1169 } | |
1170 | |
1171 if (r->headers_in.user_agent) { | |
1172 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1173 NGX_HTTP_V3_HEADER_USER_AGENT, | |
1174 r->headers_in.user_agent->value.data, | |
1175 r->headers_in.user_agent->value.len); | |
1176 } | |
1177 | |
1178 cl = ngx_alloc_chain_link(r->pool); | |
1179 if (cl == NULL) { | |
1180 return NULL; | |
1181 } | |
1182 | |
1183 cl->buf = b; | |
1184 cl->next = NULL; | |
1185 | |
1186 n = b->last - b->pos; | |
1187 | |
1188 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) | |
1189 + ngx_http_v3_encode_varlen_int(NULL, n); | |
1190 | |
1191 b = ngx_create_temp_buf(r->pool, len); | |
1192 if (b == NULL) { | |
1193 return NULL; | |
1194 } | |
1195 | |
1196 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
1197 NGX_HTTP_V3_FRAME_PUSH_PROMISE); | |
1198 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); | |
1199 | |
1200 hl = ngx_alloc_chain_link(r->pool); | |
1201 if (hl == NULL) { | |
1202 return NULL; | |
1203 } | |
1204 | |
1205 hl->buf = b; | |
1206 hl->next = cl; | |
1207 | |
1208 return hl; | |
1209 } | |
1210 | |
1211 | |
1212 static ngx_int_t | |
1213 ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
1214 { | |
1215 u_char *chunk; | |
1216 off_t size; | |
1217 ngx_int_t rc; | |
1218 ngx_buf_t *b; | |
1219 ngx_chain_t *out, *cl, *tl, **ll; | |
1220 ngx_http_v3_filter_ctx_t *ctx; | |
1221 | |
1222 if (in == NULL) { | |
1223 return ngx_http_next_body_filter(r, in); | |
1224 } | |
1225 | |
1226 ctx = ngx_http_get_module_ctx(r, ngx_http_v3_filter_module); | |
1227 if (ctx == NULL) { | |
1228 return ngx_http_next_body_filter(r, in); | |
1229 } | |
1230 | |
1231 out = NULL; | |
1232 ll = &out; | |
1233 | |
1234 size = 0; | |
1235 cl = in; | |
1236 | |
1237 for ( ;; ) { | |
1238 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1239 "http3 chunk: %O", ngx_buf_size(cl->buf)); | |
1240 | |
1241 size += ngx_buf_size(cl->buf); | |
1242 | |
1243 if (cl->buf->flush | |
1244 || cl->buf->sync | |
1245 || ngx_buf_in_memory(cl->buf) | |
1246 || cl->buf->in_file) | |
1247 { | |
1248 tl = ngx_alloc_chain_link(r->pool); | |
1249 if (tl == NULL) { | |
1250 return NGX_ERROR; | |
1251 } | |
1252 | |
1253 tl->buf = cl->buf; | |
1254 *ll = tl; | |
1255 ll = &tl->next; | |
1256 } | |
1257 | |
1258 if (cl->next == NULL) { | |
1259 break; | |
1260 } | |
1261 | |
1262 cl = cl->next; | |
1263 } | |
1264 | |
1265 if (size) { | |
1266 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
1267 if (tl == NULL) { | |
1268 return NGX_ERROR; | |
1269 } | |
1270 | |
1271 b = tl->buf; | |
1272 chunk = b->start; | |
1273 | |
1274 if (chunk == NULL) { | |
1275 chunk = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2); | |
1276 if (chunk == NULL) { | |
1277 return NGX_ERROR; | |
1278 } | |
1279 | |
1280 b->start = chunk; | |
1281 b->end = chunk + NGX_HTTP_V3_VARLEN_INT_LEN * 2; | |
1282 } | |
1283 | |
1284 b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; | |
1285 b->memory = 0; | |
1286 b->temporary = 1; | |
1287 b->pos = chunk; | |
1288 | |
1289 b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk, | |
1290 NGX_HTTP_V3_FRAME_DATA); | |
1291 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); | |
1292 | |
1293 tl->next = out; | |
1294 out = tl; | |
1295 } | |
1296 | |
1297 if (cl->buf->last_buf) { | |
1298 tl = ngx_http_v3_create_trailers(r); | |
1299 if (tl == NULL) { | |
1300 return NGX_ERROR; | |
1301 } | |
1302 | |
1303 cl->buf->last_buf = 0; | |
1304 | |
1305 *ll = tl; | |
1306 | |
1307 } else { | |
1308 *ll = NULL; | |
1309 } | |
1310 | |
1311 rc = ngx_http_next_body_filter(r, out); | |
1312 | |
1313 ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, | |
1314 (ngx_buf_tag_t) &ngx_http_v3_filter_module); | |
1315 | |
1316 return rc; | |
1317 } | |
1318 | |
1319 | |
1320 static ngx_chain_t * | |
1321 ngx_http_v3_create_trailers(ngx_http_request_t *r) | |
1322 { | |
1323 ngx_buf_t *b; | |
1324 ngx_chain_t *cl; | |
1325 | |
1326 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1327 "http3 create trailers"); | |
1328 | |
1329 /* XXX */ | |
1330 | |
1331 b = ngx_calloc_buf(r->pool); | |
1332 if (b == NULL) { | |
1333 return NULL; | |
1334 } | |
1335 | |
1336 b->last_buf = 1; | |
1337 | |
1338 cl = ngx_alloc_chain_link(r->pool); | |
1339 if (cl == NULL) { | |
1340 return NULL; | |
1341 } | |
1342 | |
1343 cl->buf = b; | |
1344 cl->next = NULL; | |
1345 | |
1346 return cl; | |
1347 } | |
1348 | |
1349 | |
1350 static ngx_int_t | |
1351 ngx_http_v3_filter_init(ngx_conf_t *cf) | |
1352 { | |
1353 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
1354 ngx_http_top_header_filter = ngx_http_v3_header_filter; | |
1355 | |
1356 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
1357 ngx_http_top_body_filter = ngx_http_v3_body_filter; | |
1358 | |
1359 return NGX_OK; | |
1360 } |