Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2_filter_module.c @ 6277:b930e598a199
HTTP/2: fixed splitting of response headers on CONTINUATION frames.
Previous code has been based on assumption that the header block can only be
splitted at the borders of individual headers. That wasn't the case and might
result in emitting frames bigger than the frame size limit.
The current approach is to split header blocks by the frame size limit.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Mon, 28 Sep 2015 02:32:44 +0300 |
parents | 0efc16d55adb |
children | c72eaf694d99 |
comparison
equal
deleted
inserted
replaced
6276:0efc16d55adb | 6277:b930e598a199 |
---|---|
41 #define NGX_HTTP_V2_VARY_INDEX 59 | 41 #define NGX_HTTP_V2_VARY_INDEX 59 |
42 | 42 |
43 | 43 |
44 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, | 44 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, |
45 ngx_uint_t value); | 45 ngx_uint_t value); |
46 static void ngx_http_v2_write_headers_head(u_char *pos, size_t length, | 46 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( |
47 ngx_uint_t sid, ngx_uint_t end_headers, ngx_uint_t end_stream); | 47 ngx_http_request_t *r, u_char *pos, u_char *end); |
48 static void ngx_http_v2_write_continuation_head(u_char *pos, size_t length, | |
49 ngx_uint_t sid, ngx_uint_t end_headers); | |
50 | 48 |
51 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, | 49 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, |
52 ngx_chain_t *in, off_t limit); | 50 ngx_chain_t *in, off_t limit); |
53 | 51 |
54 static ngx_chain_t *ngx_http_v2_filter_get_shadow( | 52 static ngx_chain_t *ngx_http_v2_filter_get_shadow( |
114 | 112 |
115 | 113 |
116 static ngx_int_t | 114 static ngx_int_t |
117 ngx_http_v2_header_filter(ngx_http_request_t *r) | 115 ngx_http_v2_header_filter(ngx_http_request_t *r) |
118 { | 116 { |
119 u_char status, *p, *head; | 117 u_char status, *pos, *start, *p; |
120 size_t len, rest, frame_size; | 118 size_t len; |
121 ngx_buf_t *b; | |
122 ngx_str_t host, location; | 119 ngx_str_t host, location; |
123 ngx_uint_t i, port, continuation; | 120 ngx_uint_t i, port; |
124 ngx_chain_t *cl; | |
125 ngx_list_part_t *part; | 121 ngx_list_part_t *part; |
126 ngx_table_elt_t *header; | 122 ngx_table_elt_t *header; |
127 ngx_connection_t *fc; | 123 ngx_connection_t *fc; |
128 ngx_http_cleanup_t *cln; | 124 ngx_http_cleanup_t *cln; |
129 ngx_http_v2_stream_t *stream; | |
130 ngx_http_v2_out_frame_t *frame; | 125 ngx_http_v2_out_frame_t *frame; |
131 ngx_http_core_loc_conf_t *clcf; | 126 ngx_http_core_loc_conf_t *clcf; |
132 ngx_http_core_srv_conf_t *cscf; | 127 ngx_http_core_srv_conf_t *cscf; |
133 struct sockaddr_in *sin; | 128 struct sockaddr_in *sin; |
134 #if (NGX_HAVE_INET6) | 129 #if (NGX_HAVE_INET6) |
386 | 381 |
387 len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len | 382 len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len |
388 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; | 383 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; |
389 } | 384 } |
390 | 385 |
391 stream = r->stream; | 386 pos = ngx_palloc(r->pool, len); |
392 frame_size = stream->connection->frame_size; | 387 if (pos == NULL) { |
393 | |
394 len += NGX_HTTP_V2_FRAME_HEADER_SIZE | |
395 * ((len + frame_size - 1) / frame_size); | |
396 | |
397 b = ngx_create_temp_buf(r->pool, len); | |
398 if (b == NULL) { | |
399 return NGX_ERROR; | 388 return NGX_ERROR; |
400 } | 389 } |
401 | 390 |
402 b->last_buf = r->header_only; | 391 start = pos; |
403 | |
404 b->last += NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
405 | 392 |
406 if (status) { | 393 if (status) { |
407 *b->last++ = status; | 394 *pos++ = status; |
408 | 395 |
409 } else { | 396 } else { |
410 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); | 397 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); |
411 *b->last++ = NGX_HTTP_V2_ENCODE_RAW | 3; | 398 *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; |
412 b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); | 399 pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); |
413 } | 400 } |
414 | 401 |
415 if (r->headers_out.server == NULL) { | 402 if (r->headers_out.server == NULL) { |
416 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); | 403 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); |
417 | 404 |
418 if (clcf->server_tokens) { | 405 if (clcf->server_tokens) { |
419 *b->last++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof(NGINX_VER) - 1); | 406 *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof(NGINX_VER) - 1); |
420 b->last = ngx_cpymem(b->last, NGINX_VER, sizeof(NGINX_VER) - 1); | 407 pos = ngx_cpymem(pos, NGINX_VER, sizeof(NGINX_VER) - 1); |
421 | 408 |
422 } else { | 409 } else { |
423 *b->last++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("nginx") - 1); | 410 *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("nginx") - 1); |
424 b->last = ngx_cpymem(b->last, "nginx", sizeof("nginx") - 1); | 411 pos = ngx_cpymem(pos, "nginx", sizeof("nginx") - 1); |
425 } | 412 } |
426 } | 413 } |
427 | 414 |
428 if (r->headers_out.date == NULL) { | 415 if (r->headers_out.date == NULL) { |
429 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); | 416 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); |
430 *b->last++ = (u_char) ngx_cached_http_time.len; | 417 *pos++ = (u_char) ngx_cached_http_time.len; |
431 | 418 |
432 b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, | 419 pos = ngx_cpymem(pos, ngx_cached_http_time.data, |
433 ngx_cached_http_time.len); | 420 ngx_cached_http_time.len); |
434 } | 421 } |
435 | 422 |
436 if (r->headers_out.content_type.len) { | 423 if (r->headers_out.content_type.len) { |
437 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); | 424 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); |
438 | 425 |
439 if (r->headers_out.content_type_len == r->headers_out.content_type.len | 426 if (r->headers_out.content_type_len == r->headers_out.content_type.len |
440 && r->headers_out.charset.len) | 427 && r->headers_out.charset.len) |
441 { | 428 { |
442 *b->last = NGX_HTTP_V2_ENCODE_RAW; | 429 *pos = NGX_HTTP_V2_ENCODE_RAW; |
443 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7), | 430 pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), |
444 r->headers_out.content_type.len | 431 r->headers_out.content_type.len |
445 + sizeof("; charset=") - 1 | 432 + sizeof("; charset=") - 1 |
446 + r->headers_out.charset.len); | 433 + r->headers_out.charset.len); |
447 | 434 |
448 p = b->last; | 435 p = pos; |
449 | 436 |
450 b->last = ngx_cpymem(p, r->headers_out.content_type.data, | 437 pos = ngx_cpymem(pos, r->headers_out.content_type.data, |
451 r->headers_out.content_type.len); | 438 r->headers_out.content_type.len); |
452 | 439 |
453 b->last = ngx_cpymem(b->last, "; charset=", | 440 pos = ngx_cpymem(pos, "; charset=", sizeof("; charset=") - 1); |
454 sizeof("; charset=") - 1); | 441 |
455 | 442 pos = ngx_cpymem(pos, r->headers_out.charset.data, |
456 b->last = ngx_cpymem(b->last, r->headers_out.charset.data, | 443 r->headers_out.charset.len); |
457 r->headers_out.charset.len); | |
458 | 444 |
459 /* update r->headers_out.content_type for possible logging */ | 445 /* update r->headers_out.content_type for possible logging */ |
460 | 446 |
461 r->headers_out.content_type.len = b->last - p; | 447 r->headers_out.content_type.len = pos - p; |
462 r->headers_out.content_type.data = p; | 448 r->headers_out.content_type.data = p; |
463 | 449 |
464 } else { | 450 } else { |
465 *b->last = NGX_HTTP_V2_ENCODE_RAW; | 451 *pos = NGX_HTTP_V2_ENCODE_RAW; |
466 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7), | 452 pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), |
467 r->headers_out.content_type.len); | 453 r->headers_out.content_type.len); |
468 b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, | 454 pos = ngx_cpymem(pos, r->headers_out.content_type.data, |
469 r->headers_out.content_type.len); | 455 r->headers_out.content_type.len); |
470 } | 456 } |
471 } | 457 } |
472 | 458 |
473 if (r->headers_out.content_length == NULL | 459 if (r->headers_out.content_length == NULL |
474 && r->headers_out.content_length_n >= 0) | 460 && r->headers_out.content_length_n >= 0) |
475 { | 461 { |
476 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); | 462 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); |
477 | 463 |
478 p = b->last; | 464 p = pos; |
479 b->last = ngx_sprintf(b->last + 1, "%O", | 465 pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); |
480 r->headers_out.content_length_n); | 466 *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); |
481 *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (b->last - p - 1); | |
482 } | 467 } |
483 | 468 |
484 if (r->headers_out.last_modified == NULL | 469 if (r->headers_out.last_modified == NULL |
485 && r->headers_out.last_modified_time != -1) | 470 && r->headers_out.last_modified_time != -1) |
486 { | 471 { |
487 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); | 472 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); |
488 | 473 |
489 *b->last++ = NGX_HTTP_V2_ENCODE_RAW | 474 *pos++ = NGX_HTTP_V2_ENCODE_RAW |
490 | (sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1); | 475 | (sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1); |
491 b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); | 476 pos = ngx_http_time(pos, r->headers_out.last_modified_time); |
492 } | 477 } |
493 | 478 |
494 if (r->headers_out.location && r->headers_out.location->value.len) { | 479 if (r->headers_out.location && r->headers_out.location->value.len) { |
495 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); | 480 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); |
496 | 481 |
497 *b->last = NGX_HTTP_V2_ENCODE_RAW; | 482 *pos = NGX_HTTP_V2_ENCODE_RAW; |
498 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7), | 483 pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), |
499 r->headers_out.location->value.len); | 484 r->headers_out.location->value.len); |
500 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, | 485 pos = ngx_cpymem(pos, r->headers_out.location->value.data, |
501 r->headers_out.location->value.len); | 486 r->headers_out.location->value.len); |
502 } | 487 } |
503 | 488 |
504 #if (NGX_HTTP_GZIP) | 489 #if (NGX_HTTP_GZIP) |
505 if (r->gzip_vary) { | 490 if (r->gzip_vary) { |
506 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); | 491 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); |
507 *b->last++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("Accept-Encoding") - 1); | 492 *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("Accept-Encoding") - 1); |
508 b->last = ngx_cpymem(b->last, "Accept-Encoding", | 493 pos = ngx_cpymem(pos, "Accept-Encoding", sizeof("Accept-Encoding") - 1); |
509 sizeof("Accept-Encoding") - 1); | |
510 } | 494 } |
511 #endif | 495 #endif |
512 | |
513 continuation = 0; | |
514 head = b->pos; | |
515 | |
516 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
517 rest = frame_size - len; | |
518 | 496 |
519 part = &r->headers_out.headers.part; | 497 part = &r->headers_out.headers.part; |
520 header = part->elts; | 498 header = part->elts; |
521 | 499 |
522 for (i = 0; /* void */; i++) { | 500 for (i = 0; /* void */; i++) { |
533 | 511 |
534 if (header[i].hash == 0) { | 512 if (header[i].hash == 0) { |
535 continue; | 513 continue; |
536 } | 514 } |
537 | 515 |
538 len = 1 + NGX_HTTP_V2_INT_OCTETS * 2 | 516 *pos++ = 0; |
539 + header[i].key.len | 517 |
540 + header[i].value.len; | 518 *pos = NGX_HTTP_V2_ENCODE_RAW; |
541 | 519 pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), |
542 if (len > rest) { | 520 header[i].key.len); |
543 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE; | 521 ngx_strlow(pos, header[i].key.data, header[i].key.len); |
544 | 522 pos += header[i].key.len; |
545 if (continuation) { | 523 |
546 ngx_http_v2_write_continuation_head(head, len, | 524 *pos = NGX_HTTP_V2_ENCODE_RAW; |
547 stream->node->id, 0); | 525 pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), |
548 } else { | 526 header[i].value.len); |
549 continuation = 1; | 527 pos = ngx_cpymem(pos, header[i].value.data, header[i].value.len); |
550 ngx_http_v2_write_headers_head(head, len, stream->node->id, 0, | 528 } |
551 r->header_only); | 529 |
552 } | 530 frame = ngx_http_v2_create_headers_frame(r, start, pos); |
553 | |
554 rest = frame_size; | |
555 head = b->last; | |
556 | |
557 b->last += NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
558 } | |
559 | |
560 p = b->last; | |
561 | |
562 *p++ = 0; | |
563 | |
564 *p = NGX_HTTP_V2_ENCODE_RAW; | |
565 p = ngx_http_v2_write_int(p, ngx_http_v2_prefix(7), header[i].key.len); | |
566 ngx_strlow(p, header[i].key.data, header[i].key.len); | |
567 p += header[i].key.len; | |
568 | |
569 *p = NGX_HTTP_V2_ENCODE_RAW; | |
570 p = ngx_http_v2_write_int(p, ngx_http_v2_prefix(7), | |
571 header[i].value.len); | |
572 p = ngx_cpymem(p, header[i].value.data, header[i].value.len); | |
573 | |
574 rest -= p - b->last; | |
575 b->last = p; | |
576 } | |
577 | |
578 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
579 | |
580 if (continuation) { | |
581 ngx_http_v2_write_continuation_head(head, len, stream->node->id, 1); | |
582 | |
583 } else { | |
584 ngx_http_v2_write_headers_head(head, len, stream->node->id, 1, | |
585 r->header_only); | |
586 } | |
587 | |
588 cl = ngx_alloc_chain_link(r->pool); | |
589 if (cl == NULL) { | |
590 return NGX_ERROR; | |
591 } | |
592 | |
593 cl->buf = b; | |
594 cl->next = NULL; | |
595 | |
596 frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); | |
597 if (frame == NULL) { | 531 if (frame == NULL) { |
598 return NGX_ERROR; | 532 return NGX_ERROR; |
599 } | 533 } |
600 | 534 |
601 frame->first = cl; | 535 ngx_http_v2_queue_blocked_frame(r->stream->connection, frame); |
602 frame->last = cl; | |
603 frame->handler = ngx_http_v2_headers_frame_handler; | |
604 frame->stream = stream; | |
605 frame->length = b->last - b->pos - NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
606 frame->blocked = 1; | |
607 frame->fin = r->header_only; | |
608 | |
609 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, | |
610 "http2:%ui create HEADERS frame %p: len:%uz", | |
611 stream->node->id, frame, frame->length); | |
612 | |
613 ngx_http_v2_queue_blocked_frame(stream->connection, frame); | |
614 | 536 |
615 cln = ngx_http_cleanup_add(r, 0); | 537 cln = ngx_http_cleanup_add(r, 0); |
616 if (cln == NULL) { | 538 if (cln == NULL) { |
617 return NGX_ERROR; | 539 return NGX_ERROR; |
618 } | 540 } |
619 | 541 |
620 cln->handler = ngx_http_v2_filter_cleanup; | 542 cln->handler = ngx_http_v2_filter_cleanup; |
621 cln->data = stream; | 543 cln->data = r->stream; |
622 | 544 |
623 stream->queued = 1; | 545 r->stream->queued = 1; |
624 | 546 |
625 fc->send_chain = ngx_http_v2_send_chain; | 547 fc->send_chain = ngx_http_v2_send_chain; |
626 fc->need_last_buf = 1; | 548 fc->need_last_buf = 1; |
627 | 549 |
628 return ngx_http_v2_filter_send(fc, stream); | 550 return ngx_http_v2_filter_send(fc, r->stream); |
629 } | 551 } |
630 | 552 |
631 | 553 |
632 static u_char * | 554 static u_char * |
633 ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) | 555 ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) |
649 | 571 |
650 return pos; | 572 return pos; |
651 } | 573 } |
652 | 574 |
653 | 575 |
654 static void | 576 static ngx_http_v2_out_frame_t * |
655 ngx_http_v2_write_headers_head(u_char *pos, size_t length, ngx_uint_t sid, | 577 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, |
656 ngx_uint_t end_headers, ngx_uint_t end_stream) | 578 u_char *end) |
657 { | 579 { |
658 u_char flags; | 580 u_char type, flags; |
659 | 581 size_t rest, frame_size; |
660 pos = ngx_http_v2_write_len_and_type(pos, length, | 582 ngx_buf_t *b; |
661 NGX_HTTP_V2_HEADERS_FRAME); | 583 ngx_chain_t *cl, **ll; |
662 | 584 ngx_http_v2_stream_t *stream; |
663 flags = NGX_HTTP_V2_NO_FLAG; | 585 ngx_http_v2_out_frame_t *frame; |
664 | 586 |
665 if (end_headers) { | 587 stream = r->stream; |
666 flags |= NGX_HTTP_V2_END_HEADERS_FLAG; | 588 rest = end - pos; |
667 } | 589 |
668 | 590 frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); |
669 if (end_stream) { | 591 if (frame == NULL) { |
670 flags |= NGX_HTTP_V2_END_STREAM_FLAG; | 592 return NULL; |
671 } | 593 } |
672 | 594 |
673 *pos++ = flags; | 595 frame->handler = ngx_http_v2_headers_frame_handler; |
674 | 596 frame->stream = stream; |
675 (void) ngx_http_v2_write_sid(pos, sid); | 597 frame->length = rest; |
676 } | 598 frame->blocked = 1; |
677 | 599 frame->fin = r->header_only; |
678 | 600 |
679 static void | 601 ll = &frame->first; |
680 ngx_http_v2_write_continuation_head(u_char *pos, size_t length, ngx_uint_t sid, | 602 |
681 ngx_uint_t end_headers) | 603 type = NGX_HTTP_V2_HEADERS_FRAME; |
682 { | 604 flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; |
683 pos = ngx_http_v2_write_len_and_type(pos, length, | 605 frame_size = stream->connection->frame_size; |
684 NGX_HTTP_V2_CONTINUATION_FRAME); | 606 |
685 | 607 for ( ;; ) { |
686 *pos++ = end_headers ? NGX_HTTP_V2_END_HEADERS_FLAG : NGX_HTTP_V2_NO_FLAG; | 608 if (rest <= frame_size) { |
687 | 609 frame_size = rest; |
688 (void) ngx_http_v2_write_sid(pos, sid); | 610 flags |= NGX_HTTP_V2_END_HEADERS_FLAG; |
611 } | |
612 | |
613 b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE); | |
614 if (b == NULL) { | |
615 return NULL; | |
616 } | |
617 | |
618 b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type); | |
619 *b->last++ = flags; | |
620 b->last = ngx_http_v2_write_sid(b->last, stream->node->id); | |
621 | |
622 cl = ngx_alloc_chain_link(r->pool); | |
623 if (cl == NULL) { | |
624 return NULL; | |
625 } | |
626 | |
627 cl->buf = b; | |
628 | |
629 *ll = cl; | |
630 ll = &cl->next; | |
631 | |
632 b = ngx_calloc_buf(r->pool); | |
633 if (b == NULL) { | |
634 return NULL; | |
635 } | |
636 | |
637 b->pos = pos; | |
638 | |
639 pos += frame_size; | |
640 | |
641 b->last = pos; | |
642 b->start = b->pos; | |
643 b->end = b->last; | |
644 b->temporary = 1; | |
645 | |
646 cl = ngx_alloc_chain_link(r->pool); | |
647 if (cl == NULL) { | |
648 return NULL; | |
649 } | |
650 | |
651 cl->buf = b; | |
652 | |
653 *ll = cl; | |
654 ll = &cl->next; | |
655 | |
656 rest -= frame_size; | |
657 | |
658 if (rest) { | |
659 type = NGX_HTTP_V2_CONTINUATION_FRAME; | |
660 flags = NGX_HTTP_V2_NO_FLAG; | |
661 continue; | |
662 } | |
663 | |
664 b->last_buf = r->header_only; | |
665 cl->next = NULL; | |
666 frame->last = cl; | |
667 | |
668 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
669 "http2:%ui create HEADERS frame %p: len:%uz", | |
670 stream->node->id, frame, frame->length); | |
671 | |
672 return frame; | |
673 } | |
689 } | 674 } |
690 | 675 |
691 | 676 |
692 static ngx_chain_t * | 677 static ngx_chain_t * |
693 ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) | 678 ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) |