comparison src/http/v2/ngx_http_v2_filter_module.c @ 9121:262c01782566

HTTP/2: removed server push (ticket #2432). Although it has better implementation status than HTTP/3 server push, it remains of limited use, with adoption numbers seen as negligible. Per IETF 102 materials, server push was used only in 0.04% of sessions. It was considered to be "difficult to use effectively" in RFC 9113. Its use is further limited by badly matching to fetch/cache/connection models in browsers, see related discussions linked from [1]. Server push was disabled in Chrome 106 [2]. The http2_push, http2_push_preload, and http2_max_concurrent_pushes directives are made obsolete. In particular, this essentially reverts 7201:641306096f5b and 7207:3d2b0b02bd3d. [1] https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/ [2] https://chromestatus.com/feature/6302414934114304
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 08 Jun 2023 16:56:46 +0400
parents ef6a3a99a81a
children 23f109f0facc
comparison
equal deleted inserted replaced
9120:0aaa09927703 9121:262c01782566
25 25
26 26
27 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 27 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
28 28
29 29
30 typedef struct {
31 ngx_str_t name;
32 u_char index;
33 ngx_uint_t offset;
34 } ngx_http_v2_push_header_t;
35
36
37 static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = {
38 { ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
39 offsetof(ngx_http_headers_in_t, host) },
40
41 { ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
42 offsetof(ngx_http_headers_in_t, accept_encoding) },
43
44 { ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
45 offsetof(ngx_http_headers_in_t, accept_language) },
46
47 { ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
48 offsetof(ngx_http_headers_in_t, user_agent) },
49 };
50
51 #define NGX_HTTP_V2_PUSH_HEADERS \
52 (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
53
54
55 static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
56 static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
57 ngx_str_t *path, ngx_str_t *binary);
58
59 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( 30 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
60 ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); 31 ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
61 static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(
62 ngx_http_request_t *r, u_char *pos, u_char *end);
63 static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( 32 static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
64 ngx_http_request_t *r); 33 ngx_http_request_t *r);
65 34
66 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, 35 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
67 ngx_chain_t *in, off_t limit); 36 ngx_chain_t *in, off_t limit);
79 48
80 static ngx_inline ngx_int_t ngx_http_v2_filter_send( 49 static ngx_inline ngx_int_t ngx_http_v2_filter_send(
81 ngx_connection_t *fc, ngx_http_v2_stream_t *stream); 50 ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
82 51
83 static ngx_int_t ngx_http_v2_headers_frame_handler( 52 static ngx_int_t ngx_http_v2_headers_frame_handler(
84 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
85 static ngx_int_t ngx_http_v2_push_frame_handler(
86 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); 53 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
87 static ngx_int_t ngx_http_v2_data_frame_handler( 54 static ngx_int_t ngx_http_v2_data_frame_handler(
88 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); 55 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
89 static ngx_inline void ngx_http_v2_handle_frame( 56 static ngx_inline void ngx_http_v2_handle_frame(
90 ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame); 57 ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
242 } 209 }
243 } 210 }
244 211
245 h2c = stream->connection; 212 h2c = stream->connection;
246 213
247 if (!h2c->push_disabled && !h2c->goaway
248 && stream->node->id % 2 == 1
249 && r->method != NGX_HTTP_HEAD)
250 {
251 if (ngx_http_v2_push_resources(r) != NGX_OK) {
252 return NGX_ERROR;
253 }
254 }
255
256 len = h2c->table_update ? 1 : 0; 214 len = h2c->table_update ? 1 : 0;
257 215
258 len += status ? 1 : 1 + ngx_http_v2_literal_size("418"); 216 len += status ? 1 : 1 + ngx_http_v2_literal_size("418");
259 217
260 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 218 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
651 return NGX_ERROR; 609 return NGX_ERROR;
652 } 610 }
653 611
654 ngx_http_v2_queue_blocked_frame(h2c, frame); 612 ngx_http_v2_queue_blocked_frame(h2c, frame);
655 613
656 stream->queued++; 614 stream->queued = 1;
657 615
658 cln = ngx_http_cleanup_add(r, 0); 616 cln = ngx_http_cleanup_add(r, 0);
659 if (cln == NULL) { 617 if (cln == NULL) {
660 return NGX_ERROR; 618 return NGX_ERROR;
661 } 619 }
666 fc->send_chain = ngx_http_v2_send_chain; 624 fc->send_chain = ngx_http_v2_send_chain;
667 fc->need_last_buf = 1; 625 fc->need_last_buf = 1;
668 fc->need_flush_buf = 1; 626 fc->need_flush_buf = 1;
669 627
670 return ngx_http_v2_filter_send(fc, stream); 628 return ngx_http_v2_filter_send(fc, stream);
671 }
672
673
674 static ngx_int_t
675 ngx_http_v2_push_resources(ngx_http_request_t *r)
676 {
677 u_char *start, *end, *last;
678 ngx_int_t rc;
679 ngx_str_t path;
680 ngx_uint_t i, push;
681 ngx_table_elt_t *h;
682 ngx_http_v2_loc_conf_t *h2lcf;
683 ngx_http_complex_value_t *pushes;
684 ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS];
685
686 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
687 "http2 push resources");
688
689 ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
690
691 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
692
693 if (h2lcf->pushes) {
694 pushes = h2lcf->pushes->elts;
695
696 for (i = 0; i < h2lcf->pushes->nelts; i++) {
697
698 if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {
699 return NGX_ERROR;
700 }
701
702 if (path.len == 0) {
703 continue;
704 }
705
706 if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
707 continue;
708 }
709
710 rc = ngx_http_v2_push_resource(r, &path, binary);
711
712 if (rc == NGX_ERROR) {
713 return NGX_ERROR;
714 }
715
716 if (rc == NGX_ABORT) {
717 return NGX_OK;
718 }
719
720 /* NGX_OK, NGX_DECLINED */
721 }
722 }
723
724 if (!h2lcf->push_preload) {
725 return NGX_OK;
726 }
727
728 for (h = r->headers_out.link; h; h = h->next) {
729
730 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
731 "http2 parse link: \"%V\"", &h->value);
732
733 start = h->value.data;
734 end = h->value.data + h->value.len;
735
736 next_link:
737
738 while (start < end && *start == ' ') { start++; }
739
740 if (start == end || *start++ != '<') {
741 continue;
742 }
743
744 while (start < end && *start == ' ') { start++; }
745
746 for (last = start; last < end && *last != '>'; last++) {
747 /* void */
748 }
749
750 if (last == start || last == end) {
751 continue;
752 }
753
754 path.len = last - start;
755 path.data = start;
756
757 start = last + 1;
758
759 while (start < end && *start == ' ') { start++; }
760
761 if (start == end) {
762 continue;
763 }
764
765 if (*start == ',') {
766 start++;
767 goto next_link;
768 }
769
770 if (*start++ != ';') {
771 continue;
772 }
773
774 last = ngx_strlchr(start, end, ',');
775
776 if (last == NULL) {
777 last = end;
778 }
779
780 push = 0;
781
782 for ( ;; ) {
783
784 while (start < last && *start == ' ') { start++; }
785
786 if (last - start >= 6
787 && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0)
788 {
789 start += 6;
790
791 if (start == last || *start == ' ' || *start == ';') {
792 push = 0;
793 break;
794 }
795
796 goto next_param;
797 }
798
799 if (last - start >= 11
800 && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0)
801 {
802 start += 11;
803
804 if (start == last || *start == ' ' || *start == ';') {
805 push = 1;
806 }
807
808 goto next_param;
809 }
810
811 if (last - start >= 4
812 && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0)
813 {
814 start += 4;
815
816 while (start < last && *start == ' ') { start++; }
817
818 if (start == last || *start++ != '"') {
819 goto next_param;
820 }
821
822 for ( ;; ) {
823
824 while (start < last && *start == ' ') { start++; }
825
826 if (last - start >= 7
827 && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0)
828 {
829 start += 7;
830
831 if (start < last && (*start == ' ' || *start == '"')) {
832 push = 1;
833 break;
834 }
835 }
836
837 while (start < last && *start != ' ' && *start != '"') {
838 start++;
839 }
840
841 if (start == last) {
842 break;
843 }
844
845 if (*start == '"') {
846 break;
847 }
848
849 start++;
850 }
851 }
852
853 next_param:
854
855 start = ngx_strlchr(start, last, ';');
856
857 if (start == NULL) {
858 break;
859 }
860
861 start++;
862 }
863
864 if (push) {
865 while (path.len && path.data[path.len - 1] == ' ') {
866 path.len--;
867 }
868 }
869
870 if (push && path.len
871 && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
872 {
873 rc = ngx_http_v2_push_resource(r, &path, binary);
874
875 if (rc == NGX_ERROR) {
876 return NGX_ERROR;
877 }
878
879 if (rc == NGX_ABORT) {
880 return NGX_OK;
881 }
882
883 /* NGX_OK, NGX_DECLINED */
884 }
885
886 if (last < end) {
887 start = last + 1;
888 goto next_link;
889 }
890 }
891
892 return NGX_OK;
893 }
894
895
896 static ngx_int_t
897 ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
898 ngx_str_t *binary)
899 {
900 u_char *start, *pos, *tmp;
901 size_t len;
902 ngx_str_t *value;
903 ngx_uint_t i;
904 ngx_table_elt_t **h;
905 ngx_connection_t *fc;
906 ngx_http_v2_stream_t *stream;
907 ngx_http_v2_out_frame_t *frame;
908 ngx_http_v2_connection_t *h2c;
909 ngx_http_v2_push_header_t *ph;
910
911 fc = r->connection;
912
913 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
914
915 stream = r->stream;
916 h2c = stream->connection;
917
918 if (!ngx_path_separator(path->data[0])) {
919 ngx_log_error(NGX_LOG_WARN, fc->log, 0,
920 "non-absolute path \"%V\" not pushed", path);
921 return NGX_DECLINED;
922 }
923
924 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
925 "http2 pushing:%ui limit:%ui",
926 h2c->pushing, h2c->concurrent_pushes);
927
928 if (h2c->pushing >= h2c->concurrent_pushes) {
929 return NGX_ABORT;
930 }
931
932 if (h2c->last_push == 0x7ffffffe) {
933 return NGX_ABORT;
934 }
935
936 if (path->len > NGX_HTTP_V2_MAX_FIELD) {
937 return NGX_DECLINED;
938 }
939
940 if (r->headers_in.host == NULL) {
941 return NGX_ABORT;
942 }
943
944 ph = ngx_http_v2_push_headers;
945
946 len = ngx_max(r->schema.len, path->len);
947
948 if (binary[0].len) {
949 tmp = ngx_palloc(r->pool, len);
950 if (tmp == NULL) {
951 return NGX_ERROR;
952 }
953
954 } else {
955 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
956 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
957
958 if (*h) {
959 len = ngx_max(len, (*h)->value.len);
960 }
961 }
962
963 tmp = ngx_palloc(r->pool, len);
964 if (tmp == NULL) {
965 return NGX_ERROR;
966 }
967
968 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
969 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
970
971 if (*h == NULL) {
972 continue;
973 }
974
975 value = &(*h)->value;
976
977 len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
978
979 pos = ngx_pnalloc(r->pool, len);
980 if (pos == NULL) {
981 return NGX_ERROR;
982 }
983
984 binary[i].data = pos;
985
986 *pos++ = ngx_http_v2_inc_indexed(ph[i].index);
987 pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
988
989 binary[i].len = pos - binary[i].data;
990 }
991 }
992
993 len = (h2c->table_update ? 1 : 0)
994 + 1
995 + 1 + NGX_HTTP_V2_INT_OCTETS + path->len
996 + 1 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;
997
998 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
999 len += binary[i].len;
1000 }
1001
1002 pos = ngx_pnalloc(r->pool, len);
1003 if (pos == NULL) {
1004 return NGX_ERROR;
1005 }
1006
1007 start = pos;
1008
1009 if (h2c->table_update) {
1010 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1011 "http2 table size update: 0");
1012 *pos++ = (1 << 5) | 0;
1013 h2c->table_update = 0;
1014 }
1015
1016 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1017 "http2 push header: \":method: GET\"");
1018
1019 *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
1020
1021 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1022 "http2 push header: \":path: %V\"", path);
1023
1024 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
1025 pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
1026
1027 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1028 "http2 push header: \":scheme: %V\"", &r->schema);
1029
1030 if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) {
1031 *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
1032
1033 } else if (r->schema.len == 4
1034 && ngx_strncmp(r->schema.data, "http", 4) == 0)
1035 {
1036 *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1037
1038 } else {
1039 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1040 pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);
1041 }
1042
1043 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1044 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
1045
1046 if (*h == NULL) {
1047 continue;
1048 }
1049
1050 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1051 "http2 push header: \"%V: %V\"",
1052 &ph[i].name, &(*h)->value);
1053
1054 pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
1055 }
1056
1057 frame = ngx_http_v2_create_push_frame(r, start, pos);
1058 if (frame == NULL) {
1059 return NGX_ERROR;
1060 }
1061
1062 ngx_http_v2_queue_blocked_frame(h2c, frame);
1063
1064 stream->queued++;
1065
1066 stream = ngx_http_v2_push_stream(stream, path);
1067
1068 if (stream) {
1069 stream->request->request_length = pos - start;
1070 return NGX_OK;
1071 }
1072
1073 return NGX_ERROR;
1074 } 629 }
1075 630
1076 631
1077 static ngx_http_v2_out_frame_t * 632 static ngx_http_v2_out_frame_t *
1078 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, 633 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
1171 frame->last = cl; 726 frame->last = cl;
1172 727
1173 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 728 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1174 "http2:%ui create HEADERS frame %p: len:%uz fin:%ui", 729 "http2:%ui create HEADERS frame %p: len:%uz fin:%ui",
1175 stream->node->id, frame, frame->length, fin); 730 stream->node->id, frame, frame->length, fin);
1176
1177 return frame;
1178 }
1179 }
1180
1181
1182 static ngx_http_v2_out_frame_t *
1183 ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
1184 {
1185 u_char type, flags;
1186 size_t rest, frame_size, len;
1187 ngx_buf_t *b;
1188 ngx_chain_t *cl, **ll;
1189 ngx_http_v2_stream_t *stream;
1190 ngx_http_v2_out_frame_t *frame;
1191 ngx_http_v2_connection_t *h2c;
1192
1193 stream = r->stream;
1194 h2c = stream->connection;
1195 rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
1196
1197 frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
1198 if (frame == NULL) {
1199 return NULL;
1200 }
1201
1202 frame->handler = ngx_http_v2_push_frame_handler;
1203 frame->stream = stream;
1204 frame->length = rest;
1205 frame->blocked = 1;
1206 frame->fin = 0;
1207
1208 ll = &frame->first;
1209
1210 type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
1211 flags = NGX_HTTP_V2_NO_FLAG;
1212 frame_size = h2c->frame_size;
1213
1214 for ( ;; ) {
1215 if (rest <= frame_size) {
1216 frame_size = rest;
1217 flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
1218 }
1219
1220 b = ngx_create_temp_buf(r->pool,
1221 NGX_HTTP_V2_FRAME_HEADER_SIZE
1222 + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
1223 ? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
1224 if (b == NULL) {
1225 return NULL;
1226 }
1227
1228 b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
1229 *b->last++ = flags;
1230 b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
1231
1232 b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1233
1234 if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
1235 h2c->last_push += 2;
1236
1237 b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
1238 len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
1239
1240 } else {
1241 len = frame_size;
1242 }
1243
1244 cl = ngx_alloc_chain_link(r->pool);
1245 if (cl == NULL) {
1246 return NULL;
1247 }
1248
1249 cl->buf = b;
1250
1251 *ll = cl;
1252 ll = &cl->next;
1253
1254 b = ngx_calloc_buf(r->pool);
1255 if (b == NULL) {
1256 return NULL;
1257 }
1258
1259 b->pos = pos;
1260
1261 pos += len;
1262
1263 b->last = pos;
1264 b->start = b->pos;
1265 b->end = b->last;
1266 b->temporary = 1;
1267
1268 cl = ngx_alloc_chain_link(r->pool);
1269 if (cl == NULL) {
1270 return NULL;
1271 }
1272
1273 cl->buf = b;
1274
1275 *ll = cl;
1276 ll = &cl->next;
1277
1278 rest -= frame_size;
1279
1280 if (rest) {
1281 frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1282
1283 type = NGX_HTTP_V2_CONTINUATION_FRAME;
1284 continue;
1285 }
1286
1287 cl->next = NULL;
1288 frame->last = cl;
1289
1290 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1291 "http2:%ui create PUSH_PROMISE frame %p: "
1292 "sid:%ui len:%uz",
1293 stream->node->id, frame, h2c->last_push,
1294 frame->length);
1295 731
1296 return frame; 732 return frame;
1297 } 733 }
1298 } 734 }
1299 735
1900 return NGX_OK; 1336 return NGX_OK;
1901 } 1337 }
1902 1338
1903 1339
1904 static ngx_int_t 1340 static ngx_int_t
1905 ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
1906 ngx_http_v2_out_frame_t *frame)
1907 {
1908 ngx_chain_t *cl, *ln;
1909 ngx_http_v2_stream_t *stream;
1910
1911 stream = frame->stream;
1912 cl = frame->first;
1913
1914 for ( ;; ) {
1915 if (cl->buf->pos != cl->buf->last) {
1916 frame->first = cl;
1917
1918 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1919 "http2:%ui PUSH_PROMISE frame %p was sent partially",
1920 stream->node->id, frame);
1921
1922 return NGX_AGAIN;
1923 }
1924
1925 ln = cl->next;
1926
1927 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1928 cl->next = stream->free_frame_headers;
1929 stream->free_frame_headers = cl;
1930
1931 } else {
1932 cl->next = stream->free_bufs;
1933 stream->free_bufs = cl;
1934 }
1935
1936 if (cl == frame->last) {
1937 break;
1938 }
1939
1940 cl = ln;
1941 }
1942
1943 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1944 "http2:%ui PUSH_PROMISE frame %p was sent",
1945 stream->node->id, frame);
1946
1947 stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1948 + frame->length;
1949
1950 h2c->payload_bytes += frame->length;
1951
1952 ngx_http_v2_handle_frame(stream, frame);
1953
1954 ngx_http_v2_handle_stream(h2c, stream);
1955
1956 return NGX_OK;
1957 }
1958
1959
1960 static ngx_int_t
1961 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c, 1341 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1962 ngx_http_v2_out_frame_t *frame) 1342 ngx_http_v2_out_frame_t *frame)
1963 { 1343 {
1964 ngx_buf_t *buf; 1344 ngx_buf_t *buf;
1965 ngx_chain_t *cl, *ln; 1345 ngx_chain_t *cl, *ln;