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