Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2_filter_module.c @ 7035:4e784e095a97
HTTP/2: added support for trailers in HTTP responses.
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
author | Piotr Sikora <piotrsikora@google.com> |
---|---|
date | Fri, 24 Mar 2017 03:37:34 -0700 |
parents | b624fbf7bee2 |
children | 12cadc4669a7 |
comparison
equal
deleted
inserted
replaced
7034:1b068a4e82d8 | 7035:4e784e095a97 |
---|---|
48 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 | 48 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 |
49 #define NGX_HTTP_V2_LOCATION_INDEX 46 | 49 #define NGX_HTTP_V2_LOCATION_INDEX 46 |
50 #define NGX_HTTP_V2_SERVER_INDEX 54 | 50 #define NGX_HTTP_V2_SERVER_INDEX 54 |
51 #define NGX_HTTP_V2_VARY_INDEX 59 | 51 #define NGX_HTTP_V2_VARY_INDEX 59 |
52 | 52 |
53 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 | |
54 | |
53 | 55 |
54 static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, | 56 static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, |
55 u_char *tmp, ngx_uint_t lower); | 57 u_char *tmp, ngx_uint_t lower); |
56 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, | 58 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, |
57 ngx_uint_t value); | 59 ngx_uint_t value); |
58 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( | 60 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( |
59 ngx_http_request_t *r, u_char *pos, u_char *end); | 61 ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); |
62 static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( | |
63 ngx_http_request_t *r); | |
60 | 64 |
61 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, | 65 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, |
62 ngx_chain_t *in, off_t limit); | 66 ngx_chain_t *in, off_t limit); |
63 | 67 |
64 static ngx_chain_t *ngx_http_v2_filter_get_shadow( | 68 static ngx_chain_t *ngx_http_v2_filter_get_shadow( |
610 | 614 |
611 pos = ngx_http_v2_write_value(pos, header[i].value.data, | 615 pos = ngx_http_v2_write_value(pos, header[i].value.data, |
612 header[i].value.len, tmp); | 616 header[i].value.len, tmp); |
613 } | 617 } |
614 | 618 |
615 frame = ngx_http_v2_create_headers_frame(r, start, pos); | 619 frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); |
616 if (frame == NULL) { | 620 if (frame == NULL) { |
617 return NGX_ERROR; | 621 return NGX_ERROR; |
618 } | 622 } |
619 | 623 |
620 ngx_http_v2_queue_blocked_frame(r->stream->connection, frame); | 624 ngx_http_v2_queue_blocked_frame(r->stream->connection, frame); |
631 | 635 |
632 fc->send_chain = ngx_http_v2_send_chain; | 636 fc->send_chain = ngx_http_v2_send_chain; |
633 fc->need_last_buf = 1; | 637 fc->need_last_buf = 1; |
634 | 638 |
635 return ngx_http_v2_filter_send(fc, r->stream); | 639 return ngx_http_v2_filter_send(fc, r->stream); |
640 } | |
641 | |
642 | |
643 static ngx_http_v2_out_frame_t * | |
644 ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) | |
645 { | |
646 u_char *pos, *start, *tmp; | |
647 size_t len, tmp_len; | |
648 ngx_uint_t i; | |
649 ngx_list_part_t *part; | |
650 ngx_table_elt_t *header; | |
651 | |
652 len = 0; | |
653 tmp_len = 0; | |
654 | |
655 part = &r->headers_out.trailers.part; | |
656 header = part->elts; | |
657 | |
658 for (i = 0; /* void */; i++) { | |
659 | |
660 if (i >= part->nelts) { | |
661 if (part->next == NULL) { | |
662 break; | |
663 } | |
664 | |
665 part = part->next; | |
666 header = part->elts; | |
667 i = 0; | |
668 } | |
669 | |
670 if (header[i].hash == 0) { | |
671 continue; | |
672 } | |
673 | |
674 if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { | |
675 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
676 "too long response trailer name: \"%V\"", | |
677 &header[i].key); | |
678 return NULL; | |
679 } | |
680 | |
681 if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { | |
682 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
683 "too long response trailer value: \"%V: %V\"", | |
684 &header[i].key, &header[i].value); | |
685 return NULL; | |
686 } | |
687 | |
688 len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len | |
689 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; | |
690 | |
691 if (header[i].key.len > tmp_len) { | |
692 tmp_len = header[i].key.len; | |
693 } | |
694 | |
695 if (header[i].value.len > tmp_len) { | |
696 tmp_len = header[i].value.len; | |
697 } | |
698 } | |
699 | |
700 if (len == 0) { | |
701 return NGX_HTTP_V2_NO_TRAILERS; | |
702 } | |
703 | |
704 tmp = ngx_palloc(r->pool, tmp_len); | |
705 pos = ngx_pnalloc(r->pool, len); | |
706 | |
707 if (pos == NULL || tmp == NULL) { | |
708 return NULL; | |
709 } | |
710 | |
711 start = pos; | |
712 | |
713 part = &r->headers_out.trailers.part; | |
714 header = part->elts; | |
715 | |
716 for (i = 0; /* void */; i++) { | |
717 | |
718 if (i >= part->nelts) { | |
719 if (part->next == NULL) { | |
720 break; | |
721 } | |
722 | |
723 part = part->next; | |
724 header = part->elts; | |
725 i = 0; | |
726 } | |
727 | |
728 if (header[i].hash == 0) { | |
729 continue; | |
730 } | |
731 | |
732 #if (NGX_DEBUG) | |
733 if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
734 ngx_strlow(tmp, header[i].key.data, header[i].key.len); | |
735 | |
736 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
737 "http2 output trailer: \"%*s: %V\"", | |
738 header[i].key.len, tmp, &header[i].value); | |
739 } | |
740 #endif | |
741 | |
742 *pos++ = 0; | |
743 | |
744 pos = ngx_http_v2_write_name(pos, header[i].key.data, | |
745 header[i].key.len, tmp); | |
746 | |
747 pos = ngx_http_v2_write_value(pos, header[i].value.data, | |
748 header[i].value.len, tmp); | |
749 } | |
750 | |
751 return ngx_http_v2_create_headers_frame(r, start, pos, 1); | |
636 } | 752 } |
637 | 753 |
638 | 754 |
639 static u_char * | 755 static u_char * |
640 ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, | 756 ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, |
684 } | 800 } |
685 | 801 |
686 | 802 |
687 static ngx_http_v2_out_frame_t * | 803 static ngx_http_v2_out_frame_t * |
688 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, | 804 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, |
689 u_char *end) | 805 u_char *end, ngx_uint_t fin) |
690 { | 806 { |
691 u_char type, flags; | 807 u_char type, flags; |
692 size_t rest, frame_size; | 808 size_t rest, frame_size; |
693 ngx_buf_t *b; | 809 ngx_buf_t *b; |
694 ngx_chain_t *cl, **ll; | 810 ngx_chain_t *cl, **ll; |
705 | 821 |
706 frame->handler = ngx_http_v2_headers_frame_handler; | 822 frame->handler = ngx_http_v2_headers_frame_handler; |
707 frame->stream = stream; | 823 frame->stream = stream; |
708 frame->length = rest; | 824 frame->length = rest; |
709 frame->blocked = 1; | 825 frame->blocked = 1; |
710 frame->fin = r->header_only; | 826 frame->fin = fin; |
711 | 827 |
712 ll = &frame->first; | 828 ll = &frame->first; |
713 | 829 |
714 type = NGX_HTTP_V2_HEADERS_FRAME; | 830 type = NGX_HTTP_V2_HEADERS_FRAME; |
715 flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; | 831 flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; |
716 frame_size = stream->connection->frame_size; | 832 frame_size = stream->connection->frame_size; |
717 | 833 |
718 for ( ;; ) { | 834 for ( ;; ) { |
719 if (rest <= frame_size) { | 835 if (rest <= frame_size) { |
720 frame_size = rest; | 836 frame_size = rest; |
774 type = NGX_HTTP_V2_CONTINUATION_FRAME; | 890 type = NGX_HTTP_V2_CONTINUATION_FRAME; |
775 flags = NGX_HTTP_V2_NO_FLAG; | 891 flags = NGX_HTTP_V2_NO_FLAG; |
776 continue; | 892 continue; |
777 } | 893 } |
778 | 894 |
779 b->last_buf = r->header_only; | 895 b->last_buf = fin; |
780 cl->next = NULL; | 896 cl->next = NULL; |
781 frame->last = cl; | 897 frame->last = cl; |
782 | 898 |
783 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 899 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
784 "http2:%ui create HEADERS frame %p: len:%uz", | 900 "http2:%ui create HEADERS frame %p: len:%uz", |
796 size_t rest, frame_size; | 912 size_t rest, frame_size; |
797 ngx_chain_t *cl, *out, **ln; | 913 ngx_chain_t *cl, *out, **ln; |
798 ngx_http_request_t *r; | 914 ngx_http_request_t *r; |
799 ngx_http_v2_stream_t *stream; | 915 ngx_http_v2_stream_t *stream; |
800 ngx_http_v2_loc_conf_t *h2lcf; | 916 ngx_http_v2_loc_conf_t *h2lcf; |
801 ngx_http_v2_out_frame_t *frame; | 917 ngx_http_v2_out_frame_t *frame, *trailers; |
802 ngx_http_v2_connection_t *h2c; | 918 ngx_http_v2_connection_t *h2c; |
803 | 919 |
804 r = fc->data; | 920 r = fc->data; |
805 stream = r->stream; | 921 stream = r->stream; |
806 | 922 |
869 | 985 |
870 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); | 986 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); |
871 | 987 |
872 frame_size = (h2lcf->chunk_size < h2c->frame_size) | 988 frame_size = (h2lcf->chunk_size < h2c->frame_size) |
873 ? h2lcf->chunk_size : h2c->frame_size; | 989 ? h2lcf->chunk_size : h2c->frame_size; |
990 | |
991 trailers = NGX_HTTP_V2_NO_TRAILERS; | |
874 | 992 |
875 #if (NGX_SUPPRESS_WARN) | 993 #if (NGX_SUPPRESS_WARN) |
876 cl = NULL; | 994 cl = NULL; |
877 #endif | 995 #endif |
878 | 996 |
932 | 1050 |
933 offset += rest; | 1051 offset += rest; |
934 size -= rest; | 1052 size -= rest; |
935 } | 1053 } |
936 | 1054 |
937 frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); | 1055 if (cl->buf->last_buf) { |
938 if (frame == NULL) { | 1056 trailers = ngx_http_v2_create_trailers_frame(r); |
939 return NGX_CHAIN_ERROR; | 1057 if (trailers == NULL) { |
940 } | 1058 return NGX_CHAIN_ERROR; |
941 | 1059 } |
942 ngx_http_v2_queue_frame(h2c, frame); | 1060 |
943 | 1061 if (trailers != NGX_HTTP_V2_NO_TRAILERS) { |
944 h2c->send_window -= frame_size; | 1062 cl->buf->last_buf = 0; |
945 | 1063 } |
946 stream->send_window -= frame_size; | 1064 } |
947 stream->queued++; | 1065 |
1066 if (frame_size || cl->buf->last_buf) { | |
1067 frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, | |
1068 out, cl); | |
1069 if (frame == NULL) { | |
1070 return NGX_CHAIN_ERROR; | |
1071 } | |
1072 | |
1073 ngx_http_v2_queue_frame(h2c, frame); | |
1074 | |
1075 h2c->send_window -= frame_size; | |
1076 | |
1077 stream->send_window -= frame_size; | |
1078 stream->queued++; | |
1079 } | |
948 | 1080 |
949 if (in == NULL) { | 1081 if (in == NULL) { |
1082 | |
1083 if (trailers != NGX_HTTP_V2_NO_TRAILERS) { | |
1084 ngx_http_v2_queue_frame(h2c, trailers); | |
1085 stream->queued++; | |
1086 } | |
1087 | |
950 break; | 1088 break; |
951 } | 1089 } |
952 | 1090 |
953 limit -= frame_size; | 1091 limit -= frame_size; |
954 | 1092 |