Mercurial > hg > nginx
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; |